From dceff27374ba8d8d036e1ec171df2fa7e2e93a96 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 21 Jan 2022 15:54:54 -0500 Subject: [PATCH 1/9] Squashed PRs 23137, 23141, 23157, 23164 --- .../src/__tests__/TimelineProfiler-test.js | 1914 +++++----- .../__snapshots__/profilingCache-test.js.snap | 14 +- .../src/__tests__/preprocessData-test.js | 3401 +++++++++-------- .../src/__tests__/store-test.js | 8 +- .../src/backend/legacy/renderer.js | 2 +- .../src/backend/profilingHooks.js | 106 +- .../src/backend/renderer.js | 43 +- .../src/backend/types.js | 1 + .../react-devtools-shared/src/constants.js | 3 + .../src/devtools/store.js | 62 +- .../Profiler/ClearProfilingDataButton.js | 21 +- .../views/Profiler/CommitTreeBuilder.js | 2 +- .../views/Profiler/NoProfilingData.js | 35 + .../src/devtools/views/Profiler/Profiler.css | 12 + .../src/devtools/views/Profiler/Profiler.js | 37 +- .../views/Profiler/ProfilerContext.js | 6 +- .../views/Profiler/ProfilingNotSupported.js | 35 + .../src/devtools/views/Profiler/types.js | 2 + .../react-devtools-timeline/src/Timeline.js | 53 +- .../src/TimelineContext.js | 28 +- .../src/TimelineNotSupported.css | 38 + .../src/TimelineNotSupported.js | 52 + 22 files changed, 3064 insertions(+), 2811 deletions(-) create mode 100644 packages/react-devtools-shared/src/devtools/views/Profiler/NoProfilingData.js create mode 100644 packages/react-devtools-shared/src/devtools/views/Profiler/ProfilingNotSupported.js create mode 100644 packages/react-devtools-timeline/src/TimelineNotSupported.css create mode 100644 packages/react-devtools-timeline/src/TimelineNotSupported.js diff --git a/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js b/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js index 1a369acca27bd..2b1908d3c29d0 100644 --- a/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js +++ b/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js @@ -18,1005 +18,766 @@ describe('Timeline profiler', () => { let unmountFns; let utils; - let clearedMarks; - let featureDetectionMarkName = null; - let marks; - let setPerformanceMock; - - function createUserTimingPolyfill() { - featureDetectionMarkName = null; - - clearedMarks = []; - marks = []; - - // Remove file-system specific bits or version-specific bits of information from the module range marks. - function filterMarkData(markName) { - if (markName.startsWith('--react-internal-module-start')) { - return `${markName.substr(0, 29)}-`; - } else if (markName.startsWith('--react-internal-module-stop')) { - return `${markName.substr(0, 28)}-`; - } else if (markName.startsWith('--react-version')) { - return `${markName.substr(0, 15)}-`; - } else { - return markName; + describe('User Timing API', () => { + let clearedMarks; + let featureDetectionMarkName = null; + let marks; + let setPerformanceMock; + + function createUserTimingPolyfill() { + featureDetectionMarkName = null; + + clearedMarks = []; + marks = []; + + // Remove file-system specific bits or version-specific bits of information from the module range marks. + function filterMarkData(markName) { + if (markName.startsWith('--react-internal-module-start')) { + return `${markName.substr(0, 29)}-`; + } else if (markName.startsWith('--react-internal-module-stop')) { + return `${markName.substr(0, 28)}-`; + } else if (markName.startsWith('--react-version')) { + return `${markName.substr(0, 15)}-`; + } else { + return markName; + } } + + // This is not a true polyfill, but it gives us enough to capture marks. + // Reference: https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API + return { + clearMarks(markName) { + markName = filterMarkData(markName); + + clearedMarks.push(markName); + marks = marks.filter(mark => mark !== markName); + }, + mark(markName, markOptions) { + markName = filterMarkData(markName); + + if (featureDetectionMarkName === null) { + featureDetectionMarkName = markName; + } + + marks.push(markName); + + if (markOptions != null) { + // This is triggers the feature detection. + markOptions.startTime++; + } + }, + }; } - // This is not a true polyfill, but it gives us enough to capture marks. - // Reference: https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API - return { - clearMarks(markName) { - markName = filterMarkData(markName); + function clearPendingMarks() { + clearedMarks.splice(0); + } - clearedMarks.push(markName); - marks = marks.filter(mark => mark !== markName); - }, - mark(markName, markOptions) { - markName = filterMarkData(markName); + function dispatchAndSetCurrentEvent(element, event) { + try { + window.event = event; + element.dispatchEvent(event); + } finally { + window.event = undefined; + } + } - if (featureDetectionMarkName === null) { - featureDetectionMarkName = markName; - } + beforeEach(() => { + utils = require('./utils'); + utils.beforeEachProfiling(); + + unmountFns = []; + renderHelper = element => { + const unmountFn = utils.legacyRender(element); + unmountFns.push(unmountFn); + return unmountFn; + }; + renderRootHelper = element => { + const container = document.createElement('div'); + const root = ReactDOM.createRoot(container); + root.render(element); + const unmountFn = () => root.unmount(); + unmountFns.push(unmountFn); + return unmountFn; + }; + + React = require('react'); + ReactDOM = require('react-dom'); + Scheduler = require('scheduler'); + + setPerformanceMock = require('react-devtools-shared/src/backend/profilingHooks') + .setPerformanceMock_ONLY_FOR_TESTING; + setPerformanceMock(createUserTimingPolyfill()); + }); - marks.push(markName); + afterEach(() => { + // Verify all logged marks also get cleared. + expect(marks).toHaveLength(0); - if (markOptions != null) { - // This is triggers the feature detection. - markOptions.startTime++; - } - }, - }; - } - - function clearPendingMarks() { - clearedMarks.splice(0); - } - - function dispatchAndSetCurrentEvent(element, event) { - try { - window.event = event; - element.dispatchEvent(event); - } finally { - window.event = undefined; - } - } - - beforeEach(() => { - utils = require('./utils'); - utils.beforeEachProfiling(); - - unmountFns = []; - renderHelper = element => { - const unmountFn = utils.legacyRender(element); - unmountFns.push(unmountFn); - return unmountFn; - }; - renderRootHelper = element => { - const container = document.createElement('div'); - const root = ReactDOM.createRoot(container); - root.render(element); - const unmountFn = () => root.unmount(); - unmountFns.push(unmountFn); - return unmountFn; - }; - - React = require('react'); - ReactDOM = require('react-dom'); - Scheduler = require('scheduler'); - - setPerformanceMock = require('react-devtools-shared/src/backend/profilingHooks') - .setPerformanceMock_ONLY_FOR_TESTING; - setPerformanceMock(createUserTimingPolyfill()); - }); + unmountFns.forEach(unmountFn => unmountFn()); - afterEach(() => { - // Verify all logged marks also get cleared. - expect(marks).toHaveLength(0); + setPerformanceMock(null); + }); - unmountFns.forEach(unmountFn => unmountFn()); + describe('when profiling', () => { + beforeEach(() => { + const store = global.store; + utils.act(() => store.profilerStore.startProfiling()); - setPerformanceMock(null); - }); + // Clear inital metadata marks to make tests below less noisy. + clearPendingMarks(); + }); - it('should mark sync render without suspends or state updates', () => { - renderHelper(
); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-1", - "--render-start-1", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-1", - "--layout-effects-stop", - "--commit-stop", - ] - `); - }); + it('should mark sync render without suspends or state updates', () => { + renderHelper(
); - it('should mark concurrent render without suspends or state updates', () => { - renderRootHelper(
); + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-1", + "--render-start-1", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-1", + "--layout-effects-stop", + "--commit-stop", + ] + `); + }); - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - ] - `); + it('should mark concurrent render without suspends or state updates', () => { + renderRootHelper(
); - clearPendingMarks(); - - expect(Scheduler).toFlushUntilNextPaint([]); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--layout-effects-stop", - "--commit-stop", - ] - `); - }); + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + ] + `); - it('should mark render yields', async () => { - function Bar() { - Scheduler.unstable_yieldValue('Bar'); - return null; - } + clearPendingMarks(); - function Foo() { - Scheduler.unstable_yieldValue('Foo'); - return ; - } + expect(Scheduler).toFlushUntilNextPaint([]); - React.startTransition(() => { - renderRootHelper(); - }); + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); + }); - // Do one step of work. - expect(Scheduler).toFlushAndYieldThrough(['Foo']); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-64", - "--render-start-64", - "--component-render-start-Foo", - "--component-render-stop", - "--render-yield", - ] - `); - }); + it('should mark render yields', async () => { + function Bar() { + Scheduler.unstable_yieldValue('Bar'); + return null; + } - it('should mark sync render with suspense that resolves', async () => { - const fakeSuspensePromise = Promise.resolve(true); - function Example() { - throw fakeSuspensePromise; - } + function Foo() { + Scheduler.unstable_yieldValue('Foo'); + return ; + } - renderHelper( - - - , - ); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-1", - "--render-start-1", - "--component-render-start-Example", - "--component-render-stop", - "--suspense-suspend-0-Example-mount-1-", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-1", - "--layout-effects-stop", - "--commit-stop", - ] - `); + React.startTransition(() => { + renderRootHelper(); + }); + + // Do one step of work. + expect(Scheduler).toFlushAndYieldThrough(['Foo']); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-64", + "--render-start-64", + "--component-render-start-Foo", + "--component-render-stop", + "--render-yield", + ] + `); + }); + + it('should mark sync render with suspense that resolves', async () => { + const fakeSuspensePromise = Promise.resolve(true); + function Example() { + throw fakeSuspensePromise; + } - clearPendingMarks(); + renderHelper( + + + , + ); - await fakeSuspensePromise; - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` Array [ - "--suspense-resolved-0-Example", + "--schedule-render-1", + "--render-start-1", + "--component-render-start-Example", + "--component-render-stop", + "--suspense-suspend-0-Example-mount-1-", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-1", + "--layout-effects-stop", + "--commit-stop", ] - `); - }); + `); - it('should mark sync render with suspense that rejects', async () => { - const fakeSuspensePromise = Promise.reject(new Error('error')); - function Example() { - throw fakeSuspensePromise; - } + clearPendingMarks(); - renderHelper( - - - , - ); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-1", - "--render-start-1", - "--component-render-start-Example", - "--component-render-stop", - "--suspense-suspend-0-Example-mount-1-", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-1", - "--layout-effects-stop", - "--commit-stop", - ] + await fakeSuspensePromise; + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--suspense-resolved-0-Example", + ] `); + }); - clearPendingMarks(); + it('should mark sync render with suspense that rejects', async () => { + const fakeSuspensePromise = Promise.reject(new Error('error')); + function Example() { + throw fakeSuspensePromise; + } - await expect(fakeSuspensePromise).rejects.toThrow(); - expect(clearedMarks).toContain(`--suspense-rejected-0-Example`); - }); + renderHelper( + + + , + ); - it('should mark concurrent render with suspense that resolves', async () => { - const fakeSuspensePromise = Promise.resolve(true); - function Example() { - throw fakeSuspensePromise; - } + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-1", + "--render-start-1", + "--component-render-start-Example", + "--component-render-stop", + "--suspense-suspend-0-Example-mount-1-", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-1", + "--layout-effects-stop", + "--commit-stop", + ] + `); - renderRootHelper( - - - , - ); + clearPendingMarks(); - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - ] - `); + await expect(fakeSuspensePromise).rejects.toThrow(); + expect(clearedMarks).toContain(`--suspense-rejected-0-Example`); + }); - clearPendingMarks(); - - expect(Scheduler).toFlushUntilNextPaint([]); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-Example", - "--component-render-stop", - "--suspense-suspend-0-Example-mount-16-", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--layout-effects-stop", - "--commit-stop", - ] - `); + it('should mark concurrent render with suspense that resolves', async () => { + const fakeSuspensePromise = Promise.resolve(true); + function Example() { + throw fakeSuspensePromise; + } - clearPendingMarks(); + renderRootHelper( + + + , + ); - await fakeSuspensePromise; - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--suspense-resolved-0-Example", - ] - `); - }); + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + ] + `); - it('should mark concurrent render with suspense that rejects', async () => { - const fakeSuspensePromise = Promise.reject(new Error('error')); - function Example() { - throw fakeSuspensePromise; - } + clearPendingMarks(); - renderRootHelper( - - - , - ); + expect(Scheduler).toFlushUntilNextPaint([]); - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - ] - `); + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--suspense-suspend-0-Example-mount-16-", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); - clearPendingMarks(); - - expect(Scheduler).toFlushUntilNextPaint([]); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-Example", - "--component-render-stop", - "--suspense-suspend-0-Example-mount-16-", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--layout-effects-stop", - "--commit-stop", - ] - `); + clearPendingMarks(); - clearPendingMarks(); + await fakeSuspensePromise; + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--suspense-resolved-0-Example", + ] + `); + }); - await expect(fakeSuspensePromise).rejects.toThrow(); - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--suspense-rejected-0-Example", - ] - `); - }); + it('should mark concurrent render with suspense that rejects', async () => { + const fakeSuspensePromise = Promise.reject(new Error('error')); + function Example() { + throw fakeSuspensePromise; + } - it('should mark cascading class component state updates', () => { - class Example extends React.Component { - state = {didMount: false}; - componentDidMount() { - this.setState({didMount: true}); - } - render() { - return null; - } - } + renderRootHelper( + + + , + ); - renderRootHelper(); + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + ] + `); - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - ] - `); + clearPendingMarks(); - clearPendingMarks(); - - expect(Scheduler).toFlushUntilNextPaint([]); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--schedule-state-update-1-Example", - "--layout-effects-stop", - "--render-start-1", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--commit-stop", - "--commit-stop", - ] - `); - }); + expect(Scheduler).toFlushUntilNextPaint([]); - it('should mark cascading class component force updates', () => { - class Example extends React.Component { - componentDidMount() { - this.forceUpdate(); - } - render() { - return null; - } - } + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--suspense-suspend-0-Example-mount-16-", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); - renderRootHelper(); + clearPendingMarks(); - expect(clearedMarks).toMatchInlineSnapshot(` + await expect(fakeSuspensePromise).rejects.toThrow(); + expect(clearedMarks).toMatchInlineSnapshot(` Array [ - "--schedule-render-16", + "--suspense-rejected-0-Example", ] `); + }); + + it('should mark cascading class component state updates', () => { + class Example extends React.Component { + state = {didMount: false}; + componentDidMount() { + this.setState({didMount: true}); + } + render() { + return null; + } + } - clearPendingMarks(); - - expect(Scheduler).toFlushUntilNextPaint([]); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--schedule-forced-update-1-Example", - "--layout-effects-stop", - "--render-start-1", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--commit-stop", - "--commit-stop", - ] - `); - }); + renderRootHelper(); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + ] + `); + + clearPendingMarks(); + + expect(Scheduler).toFlushUntilNextPaint([]); - it('should mark render phase state updates for class component', () => { - class Example extends React.Component { - state = {didRender: false}; - render() { - if (this.state.didRender === false) { - this.setState({didRender: true}); + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--schedule-state-update-1-Example", + "--layout-effects-stop", + "--render-start-1", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", + "--commit-stop", + ] + `); + }); + + it('should mark cascading class component force updates', () => { + class Example extends React.Component { + componentDidMount() { + this.forceUpdate(); + } + render() { + return null; + } } - return null; - } - } - renderRootHelper(); + renderRootHelper(); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--schedule-render-16", ] `); - clearPendingMarks(); + clearPendingMarks(); - let errorMessage; - spyOn(console, 'error').and.callFake(message => { - errorMessage = message; - }); + expect(Scheduler).toFlushUntilNextPaint([]); - expect(Scheduler).toFlushUntilNextPaint([]); - - expect(console.error).toHaveBeenCalledTimes(1); - expect(errorMessage).toContain( - 'Cannot update during an existing state transition', - ); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-Example", - "--schedule-state-update-16-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--layout-effects-stop", - "--commit-stop", - ] - `); - }); - - it('should mark render phase force updates for class component', () => { - class Example extends React.Component { - state = {didRender: false}; - render() { - if (this.state.didRender === false) { - this.forceUpdate(() => this.setState({didRender: true})); + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--schedule-forced-update-1-Example", + "--layout-effects-stop", + "--render-start-1", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", + "--commit-stop", + ] + `); + }); + + it('should mark render phase state updates for class component', () => { + class Example extends React.Component { + state = {didRender: false}; + render() { + if (this.state.didRender === false) { + this.setState({didRender: true}); + } + return null; + } } - return null; - } - } - renderRootHelper(); + renderRootHelper(); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--schedule-render-16", ] `); - clearPendingMarks(); + clearPendingMarks(); - let errorMessage; - spyOn(console, 'error').and.callFake(message => { - errorMessage = message; - }); + let errorMessage; + spyOn(console, 'error').and.callFake(message => { + errorMessage = message; + }); - expect(Scheduler).toFlushUntilNextPaint([]); - - expect(console.error).toHaveBeenCalledTimes(1); - expect(errorMessage).toContain( - 'Cannot update during an existing state transition', - ); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-Example", - "--schedule-forced-update-16-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--layout-effects-stop", - "--commit-stop", - ] - `); - }); + expect(Scheduler).toFlushUntilNextPaint([]); - it('should mark cascading layout updates', () => { - function Example() { - const [didMount, setDidMount] = React.useState(false); - React.useLayoutEffect(() => { - setDidMount(true); - }, []); - return didMount; - } + expect(console.error).toHaveBeenCalledTimes(1); + expect(errorMessage).toContain( + 'Cannot update during an existing state transition', + ); - renderRootHelper(); + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--component-render-start-Example", + "--schedule-state-update-16-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); + }); + + it('should mark render phase force updates for class component', () => { + class Example extends React.Component { + state = {didRender: false}; + render() { + if (this.state.didRender === false) { + this.forceUpdate(() => this.setState({didRender: true})); + } + return null; + } + } + + renderRootHelper(); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--schedule-render-16", ] `); - clearPendingMarks(); - - expect(Scheduler).toFlushUntilNextPaint([]); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--component-layout-effect-mount-start-Example", - "--schedule-state-update-1-Example", - "--component-layout-effect-mount-stop", - "--layout-effects-stop", - "--render-start-1", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--commit-stop", - "--commit-stop", - ] - `); - }); + clearPendingMarks(); - // This test is coupled to lane implementation details, so I'm disabling it in - // the new fork until it stabilizes so we don't have to repeatedly update it. - it('should mark cascading passive updates', () => { - function Example() { - const [didMount, setDidMount] = React.useState(false); - React.useEffect(() => { - setDidMount(true); - }, []); - return didMount; - } + let errorMessage; + spyOn(console, 'error').and.callFake(message => { + errorMessage = message; + }); - renderRootHelper(); - - expect(Scheduler).toFlushAndYield([]); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - "--render-start-16", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--layout-effects-stop", - "--commit-stop", - "--passive-effects-start-16", - "--component-passive-effect-mount-start-Example", - "--schedule-state-update-16-Example", - "--component-passive-effect-mount-stop", - "--passive-effects-stop", - "--render-start-16", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--commit-stop", - ] - `); - }); + expect(Scheduler).toFlushUntilNextPaint([]); - it('should mark render phase updates', () => { - function Example() { - const [didRender, setDidRender] = React.useState(false); - if (!didRender) { - setDidRender(true); - } - return didRender; - } + expect(console.error).toHaveBeenCalledTimes(1); + expect(errorMessage).toContain( + 'Cannot update during an existing state transition', + ); - renderRootHelper(); - - expect(Scheduler).toFlushAndYield([]); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - "--render-start-16", - "--component-render-start-Example", - "--schedule-state-update-16-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--layout-effects-stop", - "--commit-stop", - ] - `); - }); - - it('should mark sync render that throws', async () => { - spyOn(console, 'error'); - - class ErrorBoundary extends React.Component { - state = {error: null}; - componentDidCatch(error) { - this.setState({error}); - } - render() { - if (this.state.error) { - return null; + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--component-render-start-Example", + "--schedule-forced-update-16-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); + }); + + it('should mark cascading layout updates', () => { + function Example() { + const [didMount, setDidMount] = React.useState(false); + React.useLayoutEffect(() => { + setDidMount(true); + }, []); + return didMount; } - return this.props.children; - } - } - function ExampleThatThrows() { - throw Error('Expected error'); - } + renderRootHelper(); - renderHelper( - - - , - ); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-1", - "--render-start-1", - "--component-render-start-ErrorBoundary", - "--component-render-stop", - "--component-render-start-ExampleThatThrows", - "--component-render-start-ExampleThatThrows", - "--component-render-stop", - "--error-ExampleThatThrows-mount-Expected error", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-1", - "--schedule-state-update-1-ErrorBoundary", - "--layout-effects-stop", - "--commit-stop", - "--render-start-1", - "--component-render-start-ErrorBoundary", - "--component-render-stop", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--commit-stop", - ] + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + ] `); - }); - it('should mark concurrent render that throws', async () => { - spyOn(console, 'error'); + clearPendingMarks(); - class ErrorBoundary extends React.Component { - state = {error: null}; - componentDidCatch(error) { - this.setState({error}); - } - render() { - if (this.state.error) { - return null; + expect(Scheduler).toFlushUntilNextPaint([]); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--component-layout-effect-mount-start-Example", + "--schedule-state-update-1-Example", + "--component-layout-effect-mount-stop", + "--layout-effects-stop", + "--render-start-1", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", + "--commit-stop", + ] + `); + }); + + // This test is coupled to lane implementation details, so I'm disabling it in + // the new fork until it stabilizes so we don't have to repeatedly update it. + it('should mark cascading passive updates', () => { + function Example() { + const [didMount, setDidMount] = React.useState(false); + React.useEffect(() => { + setDidMount(true); + }, []); + return didMount; } - return this.props.children; - } - } - function ExampleThatThrows() { - // eslint-disable-next-line no-throw-literal - throw 'Expected error'; - } + renderRootHelper(); - renderRootHelper( - - - , - ); + expect(Scheduler).toFlushAndYield([]); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--schedule-render-16", + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + "--passive-effects-start-16", + "--component-passive-effect-mount-start-Example", + "--schedule-state-update-16-Example", + "--component-passive-effect-mount-stop", + "--passive-effects-stop", + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", ] - `); - - clearPendingMarks(); - - expect(Scheduler).toFlushUntilNextPaint([]); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-ErrorBoundary", - "--component-render-stop", - "--component-render-start-ExampleThatThrows", - "--component-render-start-ExampleThatThrows", - "--component-render-stop", - "--error-ExampleThatThrows-mount-Expected error", - "--render-stop", - "--render-start-16", - "--component-render-start-ErrorBoundary", - "--component-render-stop", - "--component-render-start-ExampleThatThrows", - "--component-render-start-ExampleThatThrows", - "--component-render-stop", - "--error-ExampleThatThrows-mount-Expected error", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--schedule-state-update-1-ErrorBoundary", - "--layout-effects-stop", - "--render-start-1", - "--component-render-start-ErrorBoundary", - "--component-render-stop", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--commit-stop", - "--commit-stop", - ] - `); - }); + `); + }); + + it('should mark render phase updates', () => { + function Example() { + const [didRender, setDidRender] = React.useState(false); + if (!didRender) { + setDidRender(true); + } + return didRender; + } - it('should mark passive and layout effects', async () => { - function ComponentWithEffects() { - React.useLayoutEffect(() => { - Scheduler.unstable_yieldValue('layout 1 mount'); - return () => { - Scheduler.unstable_yieldValue('layout 1 unmount'); - }; - }, []); - - React.useEffect(() => { - Scheduler.unstable_yieldValue('passive 1 mount'); - return () => { - Scheduler.unstable_yieldValue('passive 1 unmount'); - }; - }, []); - - React.useLayoutEffect(() => { - Scheduler.unstable_yieldValue('layout 2 mount'); - return () => { - Scheduler.unstable_yieldValue('layout 2 unmount'); - }; - }, []); - - React.useEffect(() => { - Scheduler.unstable_yieldValue('passive 2 mount'); - return () => { - Scheduler.unstable_yieldValue('passive 2 unmount'); - }; - }, []); - - React.useEffect(() => { - Scheduler.unstable_yieldValue('passive 3 mount'); - return () => { - Scheduler.unstable_yieldValue('passive 3 unmount'); - }; - }, []); - - return null; - } + renderRootHelper(); - const unmount = renderRootHelper(); - - expect(Scheduler).toFlushUntilNextPaint([ - 'layout 1 mount', - 'layout 2 mount', - ]); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - "--render-start-16", - "--component-render-start-ComponentWithEffects", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--component-layout-effect-mount-start-ComponentWithEffects", - "--component-layout-effect-mount-stop", - "--component-layout-effect-mount-start-ComponentWithEffects", - "--component-layout-effect-mount-stop", - "--layout-effects-stop", - "--commit-stop", - ] - `); + expect(Scheduler).toFlushAndYield([]); - clearPendingMarks(); - - expect(Scheduler).toFlushAndYield([ - 'passive 1 mount', - 'passive 2 mount', - 'passive 3 mount', - ]); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--passive-effects-start-16", - "--component-passive-effect-mount-start-ComponentWithEffects", - "--component-passive-effect-mount-stop", - "--component-passive-effect-mount-start-ComponentWithEffects", - "--component-passive-effect-mount-stop", - "--component-passive-effect-mount-start-ComponentWithEffects", - "--component-passive-effect-mount-stop", - "--passive-effects-stop", - ] - `); + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + "--render-start-16", + "--component-render-start-Example", + "--schedule-state-update-16-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); + }); + + it('should mark sync render that throws', async () => { + spyOn(console, 'error'); + + class ErrorBoundary extends React.Component { + state = {error: null}; + componentDidCatch(error) { + this.setState({error}); + } + render() { + if (this.state.error) { + return null; + } + return this.props.children; + } + } - clearPendingMarks(); - - expect(Scheduler).toFlushAndYield([]); - - unmount(); - - expect(Scheduler).toHaveYielded([ - 'layout 1 unmount', - 'layout 2 unmount', - 'passive 1 unmount', - 'passive 2 unmount', - 'passive 3 unmount', - ]); - - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-1", - "--render-start-1", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--component-layout-effect-unmount-start-ComponentWithEffects", - "--component-layout-effect-unmount-stop", - "--component-layout-effect-unmount-start-ComponentWithEffects", - "--component-layout-effect-unmount-stop", - "--layout-effects-start-1", - "--layout-effects-stop", - "--passive-effects-start-1", - "--component-passive-effect-unmount-start-ComponentWithEffects", - "--component-passive-effect-unmount-stop", - "--component-passive-effect-unmount-start-ComponentWithEffects", - "--component-passive-effect-unmount-stop", - "--component-passive-effect-unmount-start-ComponentWithEffects", - "--component-passive-effect-unmount-stop", - "--passive-effects-stop", - "--commit-stop", - ] - `); - }); + function ExampleThatThrows() { + throw Error('Expected error'); + } - describe('lane labels', () => { - it('regression test SyncLane', () => { - renderHelper(
); + renderHelper( + + + , + ); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--schedule-render-1", "--render-start-1", + "--component-render-start-ErrorBoundary", + "--component-render-stop", + "--component-render-start-ExampleThatThrows", + "--component-render-start-ExampleThatThrows", + "--component-render-stop", + "--error-ExampleThatThrows-mount-Expected error", "--render-stop", "--commit-start-1", "--react-version-", @@ -1025,47 +786,90 @@ describe('Timeline profiler', () => { "--react-internal-module-stop-", "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-1", + "--schedule-state-update-1-ErrorBoundary", "--layout-effects-stop", "--commit-stop", + "--render-start-1", + "--component-render-start-ErrorBoundary", + "--component-render-stop", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start-", + "--react-internal-module-stop-", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", ] `); - }); - - it('regression test DefaultLane', () => { - renderRootHelper(
); - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - ] - `); - }); - - it('regression test InputDiscreteLane', async () => { - const targetRef = React.createRef(null); + }); + + it('should mark concurrent render that throws', async () => { + spyOn(console, 'error'); + + class ErrorBoundary extends React.Component { + state = {error: null}; + componentDidCatch(error) { + this.setState({error}); + } + render() { + if (this.state.error) { + return null; + } + return this.props.children; + } + } - function App() { - const [count, setCount] = React.useState(0); - const handleClick = () => { - setCount(count + 1); - }; - return - - ); -} diff --git a/packages/react-devtools-timeline/src/Timeline.js b/packages/react-devtools-timeline/src/Timeline.js index 20feb8eb4fc65..bfd2a35f733e7 100644 --- a/packages/react-devtools-timeline/src/Timeline.js +++ b/packages/react-devtools-timeline/src/Timeline.js @@ -19,10 +19,11 @@ import { useState, } from 'react'; import {SettingsContext} from 'react-devtools-shared/src/devtools/views/Settings/SettingsContext'; +import {ProfilerContext} from 'react-devtools-shared/src/devtools/views/Profiler/ProfilerContext'; import NoProfilingData from 'react-devtools-shared/src/devtools/views/Profiler/NoProfilingData'; +import RecordingInProgress from 'react-devtools-shared/src/devtools/views/Profiler/RecordingInProgress'; import {updateColorsToMatchTheme} from './content-views/constants'; import {TimelineContext} from './TimelineContext'; -import ImportButton from './ImportButton'; import CanvasPage from './CanvasPage'; import {importFile} from './timelineCache'; import TimelineSearchInput from './TimelineSearchInput'; @@ -32,9 +33,14 @@ import {TimelineSearchContextController} from './TimelineSearchContext'; import styles from './Timeline.css'; export function Timeline(_: {||}) { - const {file, isTimelineSupported, setFile, viewState} = useContext( - TimelineContext, - ); + const { + file, + inMemoryTimelineData, + isTimelineSupported, + setFile, + viewState, + } = useContext(TimelineContext); + const {didRecordCommits, isProfiling} = useContext(ProfilerContext); const ref = useRef(null); @@ -63,22 +69,43 @@ export function Timeline(_: {||}) { }; }, [deferredTheme]); + let content = null; + if (isProfiling) { + content = ; + } else if (inMemoryTimelineData && inMemoryTimelineData.length > 0) { + // TODO (timeline) Support multiple renderers. + const timelineData = inMemoryTimelineData[0]; + + content = ( + + + + + ); + } else if (file) { + content = ( + }> + + + ); + } else if (didRecordCommits) { + content = ; + } else if (isTimelineSupported) { + content = ; + } else { + content = ; + } + return (
- {file ? ( - }> - - - ) : isTimelineSupported ? ( - - ) : ( - - )} + {content}
); } @@ -99,9 +126,15 @@ const CouldNotLoadProfile = ({error, onFileSelect}) => (
)}
- Try importing - - another Chrome performance profile. + Try importing another Chrome performance profile. +
+
+); + +const NoTimelineData = () => ( +
+
+ This current profile does not contain timeline data.
); diff --git a/packages/react-devtools-timeline/src/TimelineContext.js b/packages/react-devtools-timeline/src/TimelineContext.js index fbf52b5802479..f024ec68dccf6 100644 --- a/packages/react-devtools-timeline/src/TimelineContext.js +++ b/packages/react-devtools-timeline/src/TimelineContext.js @@ -20,6 +20,7 @@ import {StoreContext} from 'react-devtools-shared/src/devtools/views/context'; import type { HorizontalScrollStateChangeCallback, + TimelineData, SearchRegExpStateChangeCallback, ViewState, } from './types'; @@ -27,6 +28,7 @@ import type {RefObject} from 'shared/ReactTypes'; export type Context = {| file: File | null, + inMemoryTimelineData: Array | null, isTimelineSupported: boolean, searchInputContainerRef: RefObject, setFile: (file: File | null) => void, @@ -58,6 +60,20 @@ function TimelineContextController({children}: Props) { }, ); + const inMemoryTimelineData = useSyncExternalStore | null>( + function subscribe(callback) { + store.profilerStore.addListener('isProcessingData', callback); + store.profilerStore.addListener('profilingData', callback); + return function unsubscribe() { + store.profilerStore.removeListener('isProcessingData', callback); + store.profilerStore.removeListener('profilingData', callback); + }; + }, + function getState() { + return store.profilerStore.profilingData?.timelineData || null; + }, + ); + // Recreate view state any time new profiling data is imported. const viewState = useMemo(() => { const horizontalScrollStateChangeCallbacks: Set = new Set(); @@ -108,12 +124,13 @@ function TimelineContextController({children}: Props) { const value = useMemo( () => ({ file, + inMemoryTimelineData, isTimelineSupported, searchInputContainerRef, setFile, viewState, }), - [file, isTimelineSupported, setFile, viewState], + [file, inMemoryTimelineData, isTimelineSupported, setFile, viewState], ); return ( diff --git a/packages/react-devtools-timeline/src/TimelineSearchContext.js b/packages/react-devtools-timeline/src/TimelineSearchContext.js index 46d05710ac091..c545f83888e91 100644 --- a/packages/react-devtools-timeline/src/TimelineSearchContext.js +++ b/packages/react-devtools-timeline/src/TimelineSearchContext.js @@ -10,14 +10,10 @@ import * as React from 'react'; import {createContext, useMemo, useReducer} from 'react'; -import type { - ReactComponentMeasure, - ReactProfilerData, - ViewState, -} from './types'; +import type {ReactComponentMeasure, TimelineData, ViewState} from './types'; type State = {| - profilerData: ReactProfilerData, + profilerData: TimelineData, searchIndex: number, searchRegExp: RegExp | null, searchResults: Array, @@ -116,7 +112,7 @@ function reducer(state: State, action: Action): State { } export type Context = {| - profilerData: ReactProfilerData, + profilerData: TimelineData, // Search state dispatch: Dispatch, @@ -131,7 +127,7 @@ TimelineSearchContext.displayName = 'TimelineSearchContext'; type Props = {| children: React$Node, - profilerData: ReactProfilerData, + profilerData: TimelineData, viewState: ViewState, |}; diff --git a/packages/react-devtools-timeline/src/content-views/ComponentMeasuresView.js b/packages/react-devtools-timeline/src/content-views/ComponentMeasuresView.js index 2194c79e87fca..c4fd41bdfa250 100644 --- a/packages/react-devtools-timeline/src/content-views/ComponentMeasuresView.js +++ b/packages/react-devtools-timeline/src/content-views/ComponentMeasuresView.js @@ -7,11 +7,7 @@ * @flow */ -import type { - ReactComponentMeasure, - ReactProfilerData, - ViewState, -} from '../types'; +import type {ReactComponentMeasure, TimelineData, ViewState} from '../types'; import type { Interaction, IntrinsicSize, @@ -44,7 +40,7 @@ export class ComponentMeasuresView extends View { _cachedSearchRegExp: RegExp | null = null; _hoveredComponentMeasure: ReactComponentMeasure | null = null; _intrinsicSize: IntrinsicSize; - _profilerData: ReactProfilerData; + _profilerData: TimelineData; _viewState: ViewState; onHover: ((event: ReactComponentMeasure | null) => void) | null = null; @@ -52,7 +48,7 @@ export class ComponentMeasuresView extends View { constructor( surface: Surface, frame: Rect, - profilerData: ReactProfilerData, + profilerData: TimelineData, viewState: ViewState, ) { super(surface, frame); diff --git a/packages/react-devtools-timeline/src/content-views/NativeEventsView.js b/packages/react-devtools-timeline/src/content-views/NativeEventsView.js index 29e2535de6ab9..5db13dbc675e3 100644 --- a/packages/react-devtools-timeline/src/content-views/NativeEventsView.js +++ b/packages/react-devtools-timeline/src/content-views/NativeEventsView.js @@ -7,7 +7,7 @@ * @flow */ -import type {NativeEvent, ReactProfilerData} from '../types'; +import type {NativeEvent, TimelineData} from '../types'; import type { Interaction, IntrinsicSize, @@ -40,11 +40,11 @@ export class NativeEventsView extends View { _hoveredEvent: NativeEvent | null = null; _intrinsicSize: IntrinsicSize; _maxDepth: number = 0; - _profilerData: ReactProfilerData; + _profilerData: TimelineData; onHover: ((event: NativeEvent | null) => void) | null = null; - constructor(surface: Surface, frame: Rect, profilerData: ReactProfilerData) { + constructor(surface: Surface, frame: Rect, profilerData: TimelineData) { super(surface, frame); this._profilerData = profilerData; diff --git a/packages/react-devtools-timeline/src/content-views/NetworkMeasuresView.js b/packages/react-devtools-timeline/src/content-views/NetworkMeasuresView.js index 321bfcca1d5aa..547af24bb26ab 100644 --- a/packages/react-devtools-timeline/src/content-views/NetworkMeasuresView.js +++ b/packages/react-devtools-timeline/src/content-views/NetworkMeasuresView.js @@ -7,7 +7,7 @@ * @flow */ -import type {NetworkMeasure, ReactProfilerData} from '../types'; +import type {NetworkMeasure, TimelineData} from '../types'; import type { Interaction, IntrinsicSize, @@ -43,11 +43,11 @@ export class NetworkMeasuresView extends View { _hoveredNetworkMeasure: NetworkMeasure | null = null; _intrinsicSize: IntrinsicSize; _maxDepth: number = 0; - _profilerData: ReactProfilerData; + _profilerData: TimelineData; onHover: ((event: NetworkMeasure | null) => void) | null = null; - constructor(surface: Surface, frame: Rect, profilerData: ReactProfilerData) { + constructor(surface: Surface, frame: Rect, profilerData: TimelineData) { super(surface, frame); this._profilerData = profilerData; diff --git a/packages/react-devtools-timeline/src/content-views/ReactMeasuresView.js b/packages/react-devtools-timeline/src/content-views/ReactMeasuresView.js index 87a377be1a1f8..50bde3471be97 100644 --- a/packages/react-devtools-timeline/src/content-views/ReactMeasuresView.js +++ b/packages/react-devtools-timeline/src/content-views/ReactMeasuresView.js @@ -7,7 +7,7 @@ * @flow */ -import type {ReactLane, ReactMeasure, ReactProfilerData} from '../types'; +import type {ReactLane, ReactMeasure, TimelineData} from '../types'; import type { Interaction, IntrinsicSize, @@ -41,12 +41,12 @@ const MAX_ROWS_TO_SHOW_INITIALLY = 5; export class ReactMeasuresView extends View { _intrinsicSize: IntrinsicSize; _lanesToRender: ReactLane[]; - _profilerData: ReactProfilerData; + _profilerData: TimelineData; _hoveredMeasure: ReactMeasure | null = null; onHover: ((measure: ReactMeasure | null) => void) | null = null; - constructor(surface: Surface, frame: Rect, profilerData: ReactProfilerData) { + constructor(surface: Surface, frame: Rect, profilerData: TimelineData) { super(surface, frame); this._profilerData = profilerData; this._performPreflightComputations(); diff --git a/packages/react-devtools-timeline/src/content-views/SchedulingEventsView.js b/packages/react-devtools-timeline/src/content-views/SchedulingEventsView.js index 39921a727366e..3798d261e0b1b 100644 --- a/packages/react-devtools-timeline/src/content-views/SchedulingEventsView.js +++ b/packages/react-devtools-timeline/src/content-views/SchedulingEventsView.js @@ -7,7 +7,7 @@ * @flow */ -import type {SchedulingEvent, ReactProfilerData} from '../types'; +import type {SchedulingEvent, TimelineData} from '../types'; import type { Interaction, MouseMoveInteraction, @@ -40,13 +40,13 @@ const EVENT_ROW_HEIGHT_FIXED = TOP_ROW_PADDING + REACT_EVENT_DIAMETER + TOP_ROW_PADDING; export class SchedulingEventsView extends View { - _profilerData: ReactProfilerData; + _profilerData: TimelineData; _intrinsicSize: Size; _hoveredEvent: SchedulingEvent | null = null; onHover: ((event: SchedulingEvent | null) => void) | null = null; - constructor(surface: Surface, frame: Rect, profilerData: ReactProfilerData) { + constructor(surface: Surface, frame: Rect, profilerData: TimelineData) { super(surface, frame); this._profilerData = profilerData; diff --git a/packages/react-devtools-timeline/src/content-views/SnapshotsView.js b/packages/react-devtools-timeline/src/content-views/SnapshotsView.js index b027acae37693..eaaf44659ea33 100644 --- a/packages/react-devtools-timeline/src/content-views/SnapshotsView.js +++ b/packages/react-devtools-timeline/src/content-views/SnapshotsView.js @@ -7,7 +7,7 @@ * @flow */ -import type {Snapshot, ReactProfilerData} from '../types'; +import type {Snapshot, TimelineData} from '../types'; import type { Interaction, Point, @@ -31,11 +31,11 @@ type OnHover = (node: Snapshot | null) => void; export class SnapshotsView extends View { _hoverLocation: Point | null = null; _intrinsicSize: Size; - _profilerData: ReactProfilerData; + _profilerData: TimelineData; onHover: OnHover | null = null; - constructor(surface: Surface, frame: Rect, profilerData: ReactProfilerData) { + constructor(surface: Surface, frame: Rect, profilerData: TimelineData) { super(surface, frame); this._intrinsicSize = { diff --git a/packages/react-devtools-timeline/src/content-views/SuspenseEventsView.js b/packages/react-devtools-timeline/src/content-views/SuspenseEventsView.js index 58601d3590f53..370c8f763275c 100644 --- a/packages/react-devtools-timeline/src/content-views/SuspenseEventsView.js +++ b/packages/react-devtools-timeline/src/content-views/SuspenseEventsView.js @@ -7,7 +7,7 @@ * @flow */ -import type {SuspenseEvent, ReactProfilerData} from '../types'; +import type {SuspenseEvent, TimelineData} from '../types'; import type { Interaction, IntrinsicSize, @@ -47,11 +47,11 @@ export class SuspenseEventsView extends View { _hoveredEvent: SuspenseEvent | null = null; _intrinsicSize: IntrinsicSize; _maxDepth: number = 0; - _profilerData: ReactProfilerData; + _profilerData: TimelineData; onHover: ((event: SuspenseEvent | null) => void) | null = null; - constructor(surface: Surface, frame: Rect, profilerData: ReactProfilerData) { + constructor(surface: Surface, frame: Rect, profilerData: TimelineData) { super(surface, frame); this._profilerData = profilerData; diff --git a/packages/react-devtools-timeline/src/content-views/ThrownErrorsView.js b/packages/react-devtools-timeline/src/content-views/ThrownErrorsView.js index e1d084ee40935..d2a70e9524b14 100644 --- a/packages/react-devtools-timeline/src/content-views/ThrownErrorsView.js +++ b/packages/react-devtools-timeline/src/content-views/ThrownErrorsView.js @@ -7,7 +7,7 @@ * @flow */ -import type {ThrownError, ReactProfilerData} from '../types'; +import type {ThrownError, TimelineData} from '../types'; import type { Interaction, MouseMoveInteraction, @@ -40,12 +40,12 @@ const EVENT_ROW_HEIGHT_FIXED = TOP_ROW_PADDING + REACT_EVENT_DIAMETER + TOP_ROW_PADDING; export class ThrownErrorsView extends View { - _profilerData: ReactProfilerData; + _profilerData: TimelineData; _intrinsicSize: Size; _hoveredEvent: ThrownError | null = null; onHover: ((event: ThrownError | null) => void) | null = null; - constructor(surface: Surface, frame: Rect, profilerData: ReactProfilerData) { + constructor(surface: Surface, frame: Rect, profilerData: TimelineData) { super(surface, frame); this._profilerData = profilerData; diff --git a/packages/react-devtools-timeline/src/createDataResourceFromImportedFile.js b/packages/react-devtools-timeline/src/createDataResourceFromImportedFile.js index 3c7e74326094d..66ac1e3ff068f 100644 --- a/packages/react-devtools-timeline/src/createDataResourceFromImportedFile.js +++ b/packages/react-devtools-timeline/src/createDataResourceFromImportedFile.js @@ -11,17 +11,17 @@ import {createResource} from 'react-devtools-shared/src/devtools/cache'; import {importFile} from './import-worker'; import type {Resource} from 'react-devtools-shared/src/devtools/cache'; -import type {ReactProfilerData} from './types'; +import type {TimelineData} from './types'; import type {ImportWorkerOutputData} from './import-worker/index'; -export type DataResource = Resource; +export type DataResource = Resource; export default function createDataResourceFromImportedFile( file: File, ): DataResource { return createResource( () => { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const promise = ((importFile( file, ): any): Promise); diff --git a/packages/react-devtools-timeline/src/import-worker/index.js b/packages/react-devtools-timeline/src/import-worker/index.js index 3ce3a0ff93da6..636a20ed5f7b6 100644 --- a/packages/react-devtools-timeline/src/import-worker/index.js +++ b/packages/react-devtools-timeline/src/import-worker/index.js @@ -13,7 +13,7 @@ import * as importFileModule from './importFile'; import WorkerizedImportFile from './importFile.worker'; -import type {ReactProfilerData} from '../types'; +import type {TimelineData} from '../types'; type ImportFileModule = typeof importFileModule; @@ -22,7 +22,7 @@ const workerizedImportFile: ImportFileModule = window.Worker : importFileModule; export type ImportWorkerOutputData = - | {|status: 'SUCCESS', processedData: ReactProfilerData|} + | {|status: 'SUCCESS', processedData: TimelineData|} | {|status: 'INVALID_PROFILE_ERROR', error: Error|} | {|status: 'UNEXPECTED_ERROR', error: Error|}; diff --git a/packages/react-devtools-timeline/src/import-worker/preprocessData.js b/packages/react-devtools-timeline/src/import-worker/preprocessData.js index 98e802793f6a8..258eef931a382 100644 --- a/packages/react-devtools-timeline/src/import-worker/preprocessData.js +++ b/packages/react-devtools-timeline/src/import-worker/preprocessData.js @@ -25,7 +25,7 @@ import type { ReactComponentMeasureType, ReactMeasure, ReactMeasureType, - ReactProfilerData, + TimelineData, SchedulingEvent, SuspenseEvent, } from '../types'; @@ -105,7 +105,7 @@ export function getLanesFromTransportDecimalBitmask( } function updateLaneToLabelMap( - profilerData: ReactProfilerData, + profilerData: TimelineData, laneLabelTuplesString: string, ): void { // These marks appear multiple times in the data; @@ -143,7 +143,7 @@ function markWorkStarted( type: ReactMeasureType, startTime: Milliseconds, lanes: ReactLane[], - currentProfilerData: ReactProfilerData, + currentProfilerData: TimelineData, state: ProcessorState, ) { const {batchUID, measureStack} = state; @@ -179,7 +179,7 @@ function markWorkStarted( function markWorkCompleted( type: ReactMeasureType, stopTime: Milliseconds, - currentProfilerData: ReactProfilerData, + currentProfilerData: TimelineData, stack: $PropertyType, ) { if (stack.length === 0) { @@ -229,7 +229,7 @@ function throwIfIncomplete( function processEventDispatch( event: TimelineEvent, timestamp: Milliseconds, - profilerData: ReactProfilerData, + profilerData: TimelineData, state: ProcessorState, ) { const data = event.args.data; @@ -292,7 +292,7 @@ function processEventDispatch( function processResourceFinish( event: TimelineEvent, timestamp: Milliseconds, - profilerData: ReactProfilerData, + profilerData: TimelineData, state: ProcessorState, ) { const requestId = event.args.data.requestId; @@ -314,7 +314,7 @@ function processResourceFinish( function processResourceReceivedData( event: TimelineEvent, timestamp: Milliseconds, - profilerData: ReactProfilerData, + profilerData: TimelineData, state: ProcessorState, ) { const requestId = event.args.data.requestId; @@ -331,7 +331,7 @@ function processResourceReceivedData( function processResourceReceiveResponse( event: TimelineEvent, timestamp: Milliseconds, - profilerData: ReactProfilerData, + profilerData: TimelineData, state: ProcessorState, ) { const requestId = event.args.data.requestId; @@ -344,7 +344,7 @@ function processResourceReceiveResponse( function processScreenshot( event: TimelineEvent, timestamp: Milliseconds, - profilerData: ReactProfilerData, + profilerData: TimelineData, state: ProcessorState, ) { const encodedSnapshot = event.args.snapshot; // Base 64 encoded @@ -385,7 +385,7 @@ function processScreenshot( function processResourceSendRequest( event: TimelineEvent, timestamp: Milliseconds, - profilerData: ReactProfilerData, + profilerData: TimelineData, state: ProcessorState, ) { const data = event.args.data; @@ -428,7 +428,7 @@ function processResourceSendRequest( function processTimelineEvent( event: TimelineEvent, /** Finalized profiler data up to `event`. May be mutated. */ - currentProfilerData: ReactProfilerData, + currentProfilerData: TimelineData, /** Intermediate processor state. May be mutated. */ state: ProcessorState, ) { @@ -551,10 +551,7 @@ function processTimelineEvent( timestamp: startTime, type: 'thrown-error', }); - } // eslint-disable-line brace-style - - // React Events - suspense - else if (name.startsWith('--suspense-suspend-')) { + } else if (name.startsWith('--suspense-suspend-')) { const [ id, componentName, @@ -592,7 +589,6 @@ function processTimelineEvent( phase: ((phase: any): Phase), promiseName: promiseName || null, resolution: 'unresolved', - resuspendTimestamps: null, timestamp: startTime, type: 'suspense', warning: null, @@ -611,16 +607,6 @@ function processTimelineEvent( currentProfilerData.suspenseEvents.push(suspenseEvent); state.unresolvedSuspenseEvents.set(id, suspenseEvent); - } else if (name.startsWith('--suspense-resuspend-')) { - const [id] = name.substr(21).split('-'); - const suspenseEvent = state.unresolvedSuspenseEvents.get(id); - if (suspenseEvent != null) { - if (suspenseEvent.resuspendTimestamps === null) { - suspenseEvent.resuspendTimestamps = [startTime]; - } else { - suspenseEvent.resuspendTimestamps.push(startTime); - } - } } else if (name.startsWith('--suspense-resolved-')) { const [id] = name.substr(20).split('-'); const suspenseEvent = state.unresolvedSuspenseEvents.get(id); @@ -639,10 +625,7 @@ function processTimelineEvent( suspenseEvent.duration = startTime - suspenseEvent.timestamp; suspenseEvent.resolution = 'rejected'; } - } // eslint-disable-line brace-style - - // React Measures - render - else if (name.startsWith('--render-start-')) { + } else if (name.startsWith('--render-start-')) { if (state.nextRenderShouldGenerateNewBatchID) { state.nextRenderShouldGenerateNewBatchID = false; state.batchUID = ((state.uidCounter++: any): BatchUID); @@ -701,24 +684,7 @@ function processTimelineEvent( currentProfilerData, state.measureStack, ); - } else if (name.startsWith('--render-cancel')) { - state.nextRenderShouldGenerateNewBatchID = true; - markWorkCompleted( - 'render', - startTime, - currentProfilerData, - state.measureStack, - ); - markWorkCompleted( - 'render-idle', - startTime, - currentProfilerData, - state.measureStack, - ); - } // eslint-disable-line brace-style - - // React Measures - commits - else if (name.startsWith('--commit-start-')) { + } else if (name.startsWith('--commit-start-')) { state.nextRenderShouldGenerateNewBatchID = true; const [laneBitmaskString] = name.substr(15).split('-'); @@ -742,10 +708,7 @@ function processTimelineEvent( currentProfilerData, state.measureStack, ); - } // eslint-disable-line brace-style - - // React Measures - layout effects - else if (name.startsWith('--layout-effects-start-')) { + } else if (name.startsWith('--layout-effects-start-')) { const [laneBitmaskString] = name.substr(23).split('-'); markWorkStarted( @@ -762,10 +725,7 @@ function processTimelineEvent( currentProfilerData, state.measureStack, ); - } // eslint-disable-line brace-style - - // React Measures - passive effects - else if (name.startsWith('--passive-effects-start-')) { + } else if (name.startsWith('--passive-effects-start-')) { const [laneBitmaskString] = name.substr(24).split('-'); markWorkStarted( @@ -782,10 +742,7 @@ function processTimelineEvent( currentProfilerData, state.measureStack, ); - } // eslint-disable-line brace-style - - // Internal module ranges - else if (name.startsWith('--react-internal-module-start-')) { + } else if (name.startsWith('--react-internal-module-start-')) { const stackFrameStart = name.substr(30); if (!state.internalModuleStackStringSet.has(stackFrameStart)) { @@ -796,7 +753,7 @@ function processTimelineEvent( state.internalModuleCurrentStackFrame = parsedStackFrameStart; } } else if (name.startsWith('--react-internal-module-stop-')) { - const stackFrameStop = name.substr(19); + const stackFrameStop = name.substr(29); if (!state.internalModuleStackStringSet.has(stackFrameStop)) { state.internalModuleStackStringSet.add(stackFrameStop); @@ -825,10 +782,7 @@ function processTimelineEvent( } } } - } // eslint-disable-line brace-style - - // Other user timing marks/measures - else if (ph === 'R' || ph === 'n') { + } else if (ph === 'R' || ph === 'n') { // User Timing mark currentProfilerData.otherUserTimingMarks.push({ name, @@ -841,10 +795,7 @@ function processTimelineEvent( } else if (ph === 'i' || ph === 'I') { // Instant events. // Note that the capital "I" is a deprecated value that exists in Chrome Canary traces. - } // eslint-disable-line brace-style - - // Unrecognized event - else { + } else { throw new InvalidProfileError( `Unrecognized event ${JSON.stringify( event, @@ -882,7 +833,7 @@ function assertCurrentComponentMeasureType( function processReactComponentMeasure( name: string, startTime: Milliseconds, - currentProfilerData: ReactProfilerData, + currentProfilerData: TimelineData, state: ProcessorState, ): void { if (name.startsWith('--component-render-start-')) { @@ -1048,7 +999,7 @@ function parseStackFrame(stackFrame: string): ErrorStackFrame | null { export default async function preprocessData( timeline: TimelineEvent[], -): Promise { +): Promise { const flamechart = preprocessFlamechart(timeline); const laneToReactMeasureMap = new Map(); @@ -1056,7 +1007,7 @@ export default async function preprocessData( laneToReactMeasureMap.set(lane, []); } - const profilerData: ReactProfilerData = { + const profilerData: TimelineData = { batchUIDToMeasuresMap: new Map(), componentMeasures: [], duration: 0, diff --git a/packages/react-devtools-timeline/src/timelineCache.js b/packages/react-devtools-timeline/src/timelineCache.js index 4e0948a48193c..0616f70008640 100644 --- a/packages/react-devtools-timeline/src/timelineCache.js +++ b/packages/react-devtools-timeline/src/timelineCache.js @@ -8,7 +8,7 @@ */ import type {Wakeable} from 'shared/ReactTypes'; -import type {ReactProfilerData} from './types'; +import type {TimelineData} from './types'; import {importFile as importFileWorker} from './import-worker'; @@ -36,10 +36,7 @@ type Record = PendingRecord | ResolvedRecord | RejectedRecord; // This is intentionally a module-level Map, rather than a React-managed one. // Otherwise, refreshing the inspected element cache would also clear this cache. // Profiler file contents are static anyway. -const fileNameToProfilerDataMap: Map< - string, - Record, -> = new Map(); +const fileNameToProfilerDataMap: Map> = new Map(); function readRecord(record: Record): ResolvedRecord | RejectedRecord { if (record.status === Resolved) { @@ -53,7 +50,7 @@ function readRecord(record: Record): ResolvedRecord | RejectedRecord { } } -export function importFile(file: File): ReactProfilerData | Error { +export function importFile(file: File): TimelineData | Error { const fileName = file.name; let record = fileNameToProfilerDataMap.get(fileName); @@ -74,7 +71,7 @@ export function importFile(file: File): ReactProfilerData | Error { callbacks.clear(); }; - const newRecord: Record = (record = { + const newRecord: Record = (record = { status: Pending, value: wakeable, }); @@ -82,7 +79,7 @@ export function importFile(file: File): ReactProfilerData | Error { importFileWorker(file).then(data => { switch (data.status) { case 'SUCCESS': - const resolvedRecord = ((newRecord: any): ResolvedRecord); + const resolvedRecord = ((newRecord: any): ResolvedRecord); resolvedRecord.status = Resolved; resolvedRecord.value = data.processedData; break; diff --git a/packages/react-devtools-timeline/src/types.js b/packages/react-devtools-timeline/src/types.js index 3e9867c8b8c65..39401ae0096a0 100644 --- a/packages/react-devtools-timeline/src/types.js +++ b/packages/react-devtools-timeline/src/types.js @@ -68,7 +68,6 @@ export type SuspenseEvent = {| +phase: Phase | null, promiseName: string | null, resolution: 'rejected' | 'resolved' | 'unresolved', - resuspendTimestamps: Array | null, +type: 'suspense', |}; @@ -196,13 +195,15 @@ export type InternalModuleSourceToRanges = Map< Array<[ErrorStackFrame, ErrorStackFrame]>, >; -export type ReactProfilerData = {| +export type LaneToLabelMap = Map; + +export type TimelineData = {| batchUIDToMeasuresMap: Map, componentMeasures: ReactComponentMeasure[], duration: number, flamechart: Flamechart, internalModuleSourceToRanges: InternalModuleSourceToRanges, - laneToLabelMap: Map, + laneToLabelMap: LaneToLabelMap, laneToReactMeasureMap: Map, nativeEvents: NativeEvent[], networkMeasures: NetworkMeasure[], @@ -216,6 +217,28 @@ export type ReactProfilerData = {| thrownErrors: ThrownError[], |}; +export type TimelineDataExport = {| + batchUIDToMeasuresMap: Array<[BatchUID, ReactMeasure[]]>, + componentMeasures: ReactComponentMeasure[], + duration: number, + flamechart: Flamechart, + internalModuleSourceToRanges: Array< + [string, Array<[ErrorStackFrame, ErrorStackFrame]>], + >, + laneToLabelMap: Array<[ReactLane, string]>, + laneToReactMeasureMap: Array<[ReactLane, ReactMeasure[]]>, + nativeEvents: NativeEvent[], + networkMeasures: NetworkMeasure[], + otherUserTimingMarks: UserTimingMark[], + reactVersion: string | null, + schedulingEvents: SchedulingEvent[], + snapshots: Snapshot[], + snapshotHeight: number, + startTime: number, + suspenseEvents: SuspenseEvent[], + thrownErrors: ThrownError[], +|}; + export type ReactHoverContextInfo = {| componentMeasure: ReactComponentMeasure | null, flamechartStackFrame: FlamechartStackFrame | null, diff --git a/packages/react-devtools-timeline/src/utils/getBatchRange.js b/packages/react-devtools-timeline/src/utils/getBatchRange.js index 840a105f8d687..8dd425d263e05 100644 --- a/packages/react-devtools-timeline/src/utils/getBatchRange.js +++ b/packages/react-devtools-timeline/src/utils/getBatchRange.js @@ -13,12 +13,12 @@ import type { BatchUID, Milliseconds, ReactMeasure, - ReactProfilerData, + TimelineData, } from '../types'; function unmemoizedGetBatchRange( batchUID: BatchUID, - data: ReactProfilerData, + data: TimelineData, minStartTime?: number = 0, ): [Milliseconds, Milliseconds] { const measures = data.batchUIDToMeasuresMap.get(batchUID); diff --git a/scripts/jest/config.build-devtools.js b/scripts/jest/config.build-devtools.js index 276d209054745..a4a2b3fbb7f89 100644 --- a/scripts/jest/config.build-devtools.js +++ b/scripts/jest/config.build-devtools.js @@ -70,6 +70,9 @@ module.exports = Object.assign({}, baseConfig, { require.resolve( '../../packages/react-devtools-shared/src/__tests__/__serializers__/inspectedElementSerializer.js' ), + require.resolve( + '../../packages/react-devtools-shared/src/__tests__/__serializers__/profilingSerializer.js' + ), require.resolve( '../../packages/react-devtools-shared/src/__tests__/__serializers__/storeSerializer.js' ), From 912830704353866427f22c9c20185522db949b0f Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 27 Jan 2022 10:22:39 -0500 Subject: [PATCH 4/9] Updated test snapshots --- .../src/__tests__/TimelineProfiler-test.js | 1043 +-- .../__serializers__/profilingSerializer.js | 25 + .../__serializers__/timelineDataSerializer.js | 29 + .../__snapshots__/profilingCache-test.js.snap | 6121 +++++++++++++---- .../src/__tests__/preprocessData-test.js | 572 +- .../src/__tests__/utils.js | 3 + scripts/jest/config.build-devtools.js | 3 + 7 files changed, 5653 insertions(+), 2143 deletions(-) create mode 100644 packages/react-devtools-shared/src/__tests__/__serializers__/profilingSerializer.js create mode 100644 packages/react-devtools-shared/src/__tests__/__serializers__/timelineDataSerializer.js diff --git a/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js b/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js index 45c767f98072c..914834f3b2d02 100644 --- a/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js +++ b/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js @@ -15,9 +15,36 @@ describe('Timeline profiler', () => { let Scheduler; let renderHelper; let renderRootHelper; + let store; let unmountFns; let utils; + beforeEach(() => { + utils = require('./utils'); + utils.beforeEachProfiling(); + + unmountFns = []; + renderHelper = element => { + const unmountFn = utils.legacyRender(element); + unmountFns.push(unmountFn); + return unmountFn; + }; + renderRootHelper = element => { + const container = document.createElement('div'); + const root = ReactDOM.createRoot(container); + root.render(element); + const unmountFn = () => root.unmount(); + unmountFns.push(unmountFn); + return unmountFn; + }; + + React = require('react'); + ReactDOM = require('react-dom'); + Scheduler = require('scheduler'); + + store = global.store; + }); + describe('User Timing API', () => { let clearedMarks; let featureDetectionMarkName = null; @@ -33,11 +60,11 @@ describe('Timeline profiler', () => { // Remove file-system specific bits or version-specific bits of information from the module range marks. function filterMarkData(markName) { if (markName.startsWith('--react-internal-module-start')) { - return `${markName.substr(0, 29)}-`; + return '--react-internal-module-start- at filtered (:0:0)'; } else if (markName.startsWith('--react-internal-module-stop')) { - return `${markName.substr(0, 28)}-`; + return '--react-internal-module-stop- at filtered (:1:1)'; } else if (markName.startsWith('--react-version')) { - return `${markName.substr(0, 15)}-`; + return '--react-version-'; } else { return markName; } @@ -83,28 +110,6 @@ describe('Timeline profiler', () => { } beforeEach(() => { - utils = require('./utils'); - utils.beforeEachProfiling(); - - unmountFns = []; - renderHelper = element => { - const unmountFn = utils.legacyRender(element); - unmountFns.push(unmountFn); - return unmountFn; - }; - renderRootHelper = element => { - const container = document.createElement('div'); - const root = ReactDOM.createRoot(container); - root.render(element); - const unmountFn = () => root.unmount(); - unmountFns.push(unmountFn); - return unmountFn; - }; - - React = require('react'); - ReactDOM = require('react-dom'); - Scheduler = require('scheduler'); - setPerformanceMock = require('react-devtools-shared/src/backend/profilingHooks') .setPerformanceMock_ONLY_FOR_TESTING; setPerformanceMock(createUserTimingPolyfill()); @@ -121,7 +126,6 @@ describe('Timeline profiler', () => { describe('when profiling', () => { beforeEach(() => { - const store = global.store; utils.act(() => store.profilerStore.startProfiling()); // Clear inital metadata marks to make tests below less noisy. @@ -132,21 +136,21 @@ describe('Timeline profiler', () => { renderHelper(
); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-1", - "--render-start-1", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-1", - "--layout-effects-stop", - "--commit-stop", - ] - `); + Array [ + "--schedule-render-1", + "--render-start-1", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-1", + "--layout-effects-stop", + "--commit-stop", + ] + `); }); it('should mark concurrent render without suspends or state updates', () => { @@ -163,20 +167,20 @@ describe('Timeline profiler', () => { expect(Scheduler).toFlushUntilNextPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--layout-effects-stop", - "--commit-stop", - ] - `); + Array [ + "--render-start-16", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); }); it('should mark render yields', async () => { @@ -221,33 +225,33 @@ describe('Timeline profiler', () => { ); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-1", - "--render-start-1", - "--component-render-start-Example", - "--component-render-stop", - "--suspense-suspend-0-Example-mount-1-", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-1", - "--layout-effects-stop", - "--commit-stop", - ] - `); + Array [ + "--schedule-render-1", + "--render-start-1", + "--component-render-start-Example", + "--component-render-stop", + "--suspense-suspend-0-Example-mount-1-", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-1", + "--layout-effects-stop", + "--commit-stop", + ] + `); clearPendingMarks(); await fakeSuspensePromise; expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--suspense-resolved-0-Example", - ] - `); + Array [ + "--suspense-resolved-0-Example", + ] + `); }); it('should mark sync render with suspense that rejects', async () => { @@ -263,24 +267,24 @@ describe('Timeline profiler', () => { ); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-1", - "--render-start-1", - "--component-render-start-Example", - "--component-render-stop", - "--suspense-suspend-0-Example-mount-1-", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-1", - "--layout-effects-stop", - "--commit-stop", - ] - `); + Array [ + "--schedule-render-1", + "--render-start-1", + "--component-render-start-Example", + "--component-render-stop", + "--suspense-suspend-0-Example-mount-1-", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-1", + "--layout-effects-stop", + "--commit-stop", + ] + `); clearPendingMarks(); @@ -311,23 +315,23 @@ describe('Timeline profiler', () => { expect(Scheduler).toFlushUntilNextPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-Example", - "--component-render-stop", - "--suspense-suspend-0-Example-mount-16-", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--layout-effects-stop", - "--commit-stop", - ] - `); + Array [ + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--suspense-suspend-0-Example-mount-16-", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); clearPendingMarks(); @@ -362,32 +366,32 @@ describe('Timeline profiler', () => { expect(Scheduler).toFlushUntilNextPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-Example", - "--component-render-stop", - "--suspense-suspend-0-Example-mount-16-", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--layout-effects-stop", - "--commit-stop", - ] - `); + Array [ + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--suspense-suspend-0-Example-mount-16-", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); clearPendingMarks(); await expect(fakeSuspensePromise).rejects.toThrow(); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--suspense-rejected-0-Example", - ] - `); + Array [ + "--suspense-rejected-0-Example", + ] + `); }); it('should mark cascading class component state updates', () => { @@ -414,34 +418,34 @@ describe('Timeline profiler', () => { expect(Scheduler).toFlushUntilNextPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--schedule-state-update-1-Example", - "--layout-effects-stop", - "--render-start-1", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--commit-stop", - "--commit-stop", - ] - `); + Array [ + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--schedule-state-update-1-Example", + "--layout-effects-stop", + "--render-start-1", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", + "--commit-stop", + ] + `); }); it('should mark cascading class component force updates', () => { @@ -457,44 +461,44 @@ describe('Timeline profiler', () => { renderRootHelper(); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - ] - `); + Array [ + "--schedule-render-16", + ] + `); clearPendingMarks(); expect(Scheduler).toFlushUntilNextPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--schedule-forced-update-1-Example", - "--layout-effects-stop", - "--render-start-1", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--commit-stop", - "--commit-stop", - ] - `); + Array [ + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--schedule-forced-update-1-Example", + "--layout-effects-stop", + "--render-start-1", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", + "--commit-stop", + ] + `); }); it('should mark render phase state updates for class component', () => { @@ -511,10 +515,10 @@ describe('Timeline profiler', () => { renderRootHelper(); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - ] - `); + Array [ + "--schedule-render-16", + ] + `); clearPendingMarks(); @@ -531,31 +535,32 @@ describe('Timeline profiler', () => { ); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-Example", - "--schedule-state-update-16-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--layout-effects-stop", - "--commit-stop", - ] - `); + Array [ + "--render-start-16", + "--component-render-start-Example", + "--schedule-state-update-16-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); }); it('should mark render phase force updates for class component', () => { + let forced = false; class Example extends React.Component { - state = {didRender: false}; render() { - if (this.state.didRender === false) { - this.forceUpdate(() => this.setState({didRender: true})); + if (!forced) { + forced = true; + this.forceUpdate(); } return null; } @@ -564,10 +569,10 @@ describe('Timeline profiler', () => { renderRootHelper(); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - ] - `); + Array [ + "--schedule-render-16", + ] + `); clearPendingMarks(); @@ -584,23 +589,23 @@ describe('Timeline profiler', () => { ); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-Example", - "--schedule-forced-update-16-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--layout-effects-stop", - "--commit-stop", - ] - `); + Array [ + "--render-start-16", + "--component-render-start-Example", + "--schedule-forced-update-16-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); }); it('should mark cascading layout updates', () => { @@ -615,50 +620,48 @@ describe('Timeline profiler', () => { renderRootHelper(); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - ] - `); + Array [ + "--schedule-render-16", + ] + `); clearPendingMarks(); expect(Scheduler).toFlushUntilNextPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--component-layout-effect-mount-start-Example", - "--schedule-state-update-1-Example", - "--component-layout-effect-mount-stop", - "--layout-effects-stop", - "--render-start-1", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--commit-stop", - "--commit-stop", - ] - `); + Array [ + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--component-layout-effect-mount-start-Example", + "--schedule-state-update-1-Example", + "--component-layout-effect-mount-stop", + "--layout-effects-stop", + "--render-start-1", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", + "--commit-stop", + ] + `); }); - // This test is coupled to lane implementation details, so I'm disabling it in - // the new fork until it stabilizes so we don't have to repeatedly update it. it('should mark cascading passive updates', () => { function Example() { const [didMount, setDidMount] = React.useState(false); @@ -673,39 +676,39 @@ describe('Timeline profiler', () => { expect(Scheduler).toFlushAndYield([]); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - "--render-start-16", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--layout-effects-stop", - "--commit-stop", - "--passive-effects-start-16", - "--component-passive-effect-mount-start-Example", - "--schedule-state-update-16-Example", - "--component-passive-effect-mount-stop", - "--passive-effects-stop", - "--render-start-16", - "--component-render-start-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--commit-stop", - ] - `); + Array [ + "--schedule-render-16", + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + "--passive-effects-start-16", + "--component-passive-effect-mount-start-Example", + "--schedule-state-update-16-Example", + "--component-passive-effect-mount-stop", + "--passive-effects-stop", + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", + ] + `); }); it('should mark render phase updates', () => { @@ -722,24 +725,24 @@ describe('Timeline profiler', () => { expect(Scheduler).toFlushAndYield([]); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - "--render-start-16", - "--component-render-start-Example", - "--schedule-state-update-16-Example", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--layout-effects-stop", - "--commit-stop", - ] - `); + Array [ + "--schedule-render-16", + "--render-start-16", + "--component-render-start-Example", + "--schedule-state-update-16-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); }); it('should mark sync render that throws', async () => { @@ -769,39 +772,39 @@ describe('Timeline profiler', () => { ); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-1", - "--render-start-1", - "--component-render-start-ErrorBoundary", - "--component-render-stop", - "--component-render-start-ExampleThatThrows", - "--component-render-start-ExampleThatThrows", - "--component-render-stop", - "--error-ExampleThatThrows-mount-Expected error", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-1", - "--schedule-state-update-1-ErrorBoundary", - "--layout-effects-stop", - "--commit-stop", - "--render-start-1", - "--component-render-start-ErrorBoundary", - "--component-render-stop", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--commit-stop", - ] - `); + Array [ + "--schedule-render-1", + "--render-start-1", + "--component-render-start-ErrorBoundary", + "--component-render-stop", + "--component-render-start-ExampleThatThrows", + "--component-render-start-ExampleThatThrows", + "--component-render-stop", + "--error-ExampleThatThrows-mount-Expected error", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-1", + "--schedule-state-update-1-ErrorBoundary", + "--layout-effects-stop", + "--commit-stop", + "--render-start-1", + "--component-render-start-ErrorBoundary", + "--component-render-stop", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", + ] + `); }); it('should mark concurrent render that throws', async () => { @@ -832,56 +835,56 @@ describe('Timeline profiler', () => { ); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - ] - `); + Array [ + "--schedule-render-16", + ] + `); clearPendingMarks(); expect(Scheduler).toFlushUntilNextPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--component-render-start-ErrorBoundary", - "--component-render-stop", - "--component-render-start-ExampleThatThrows", - "--component-render-start-ExampleThatThrows", - "--component-render-stop", - "--error-ExampleThatThrows-mount-Expected error", - "--render-stop", - "--render-start-16", - "--component-render-start-ErrorBoundary", - "--component-render-stop", - "--component-render-start-ExampleThatThrows", - "--component-render-start-ExampleThatThrows", - "--component-render-stop", - "--error-ExampleThatThrows-mount-Expected error", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--schedule-state-update-1-ErrorBoundary", - "--layout-effects-stop", - "--render-start-1", - "--component-render-start-ErrorBoundary", - "--component-render-stop", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--commit-stop", - "--commit-stop", - ] - `); + Array [ + "--render-start-16", + "--component-render-start-ErrorBoundary", + "--component-render-stop", + "--component-render-start-ExampleThatThrows", + "--component-render-start-ExampleThatThrows", + "--component-render-stop", + "--error-ExampleThatThrows-mount-Expected error", + "--render-stop", + "--render-start-16", + "--component-render-start-ErrorBoundary", + "--component-render-stop", + "--component-render-start-ExampleThatThrows", + "--component-render-start-ExampleThatThrows", + "--component-render-stop", + "--error-ExampleThatThrows-mount-Expected error", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--schedule-state-update-1-ErrorBoundary", + "--layout-effects-stop", + "--render-start-1", + "--component-render-start-ErrorBoundary", + "--component-render-stop", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", + "--commit-stop", + ] + `); }); it('should mark passive and layout effects', async () => { @@ -932,27 +935,27 @@ describe('Timeline profiler', () => { ]); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - "--render-start-16", - "--component-render-start-ComponentWithEffects", - "--component-render-stop", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--component-layout-effect-mount-start-ComponentWithEffects", - "--component-layout-effect-mount-stop", - "--component-layout-effect-mount-start-ComponentWithEffects", - "--component-layout-effect-mount-stop", - "--layout-effects-stop", - "--commit-stop", - ] - `); + Array [ + "--schedule-render-16", + "--render-start-16", + "--component-render-start-ComponentWithEffects", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--component-layout-effect-mount-start-ComponentWithEffects", + "--component-layout-effect-mount-stop", + "--component-layout-effect-mount-start-ComponentWithEffects", + "--component-layout-effect-mount-stop", + "--layout-effects-stop", + "--commit-stop", + ] + `); clearPendingMarks(); @@ -990,40 +993,6 @@ describe('Timeline profiler', () => { ]); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-1", - "--render-start-1", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--component-layout-effect-unmount-start-ComponentWithEffects", - "--component-layout-effect-unmount-stop", - "--component-layout-effect-unmount-start-ComponentWithEffects", - "--component-layout-effect-unmount-stop", - "--layout-effects-start-1", - "--layout-effects-stop", - "--passive-effects-start-1", - "--component-passive-effect-unmount-start-ComponentWithEffects", - "--component-passive-effect-unmount-stop", - "--component-passive-effect-unmount-start-ComponentWithEffects", - "--component-passive-effect-unmount-stop", - "--component-passive-effect-unmount-start-ComponentWithEffects", - "--component-passive-effect-unmount-stop", - "--passive-effects-stop", - "--commit-stop", - ] - `); - }); - - describe('lane labels', () => { - it('regression test SyncLane', () => { - renderHelper(
); - - expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--schedule-render-1", "--render-start-1", @@ -1031,23 +1000,57 @@ describe('Timeline profiler', () => { "--commit-start-1", "--react-version-", "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--component-layout-effect-unmount-start-ComponentWithEffects", + "--component-layout-effect-unmount-stop", + "--component-layout-effect-unmount-start-ComponentWithEffects", + "--component-layout-effect-unmount-stop", "--layout-effects-start-1", "--layout-effects-stop", + "--passive-effects-start-1", + "--component-passive-effect-unmount-start-ComponentWithEffects", + "--component-passive-effect-unmount-stop", + "--component-passive-effect-unmount-start-ComponentWithEffects", + "--component-passive-effect-unmount-stop", + "--component-passive-effect-unmount-start-ComponentWithEffects", + "--component-passive-effect-unmount-stop", + "--passive-effects-stop", "--commit-stop", ] `); + }); + + describe('lane labels', () => { + it('regression test SyncLane', () => { + renderHelper(
); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-1", + "--render-start-1", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-1", + "--layout-effects-stop", + "--commit-stop", + ] + `); }); it('regression test DefaultLane', () => { renderRootHelper(
); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - ] - `); + Array [ + "--schedule-render-16", + ] + `); }); it('regression test InputDiscreteLane', async () => { @@ -1072,23 +1075,23 @@ describe('Timeline profiler', () => { await Promise.resolve(); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-state-update-1-App", - "--render-start-1", - "--component-render-start-App", - "--component-render-stop", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-1", - "--layout-effects-stop", - "--commit-stop", - ] - `); + Array [ + "--schedule-state-update-1-App", + "--render-start-1", + "--component-render-start-App", + "--component-render-stop", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-1", + "--layout-effects-stop", + "--commit-stop", + ] + `); }); it('regression test InputContinuousLane', async () => { @@ -1112,23 +1115,23 @@ describe('Timeline profiler', () => { expect(Scheduler).toFlushAndYield([]); expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-state-update-4-App", - "--render-start-4", - "--component-render-start-App", - "--component-render-stop", - "--render-stop", - "--commit-start-4", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start-", - "--react-internal-module-stop-", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-4", - "--layout-effects-stop", - "--commit-stop", - ] - `); + Array [ + "--schedule-state-update-4-App", + "--render-start-4", + "--component-render-start-App", + "--component-render-stop", + "--render-stop", + "--commit-start-4", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-4", + "--layout-effects-stop", + "--commit-stop", + ] + `); }); }); }); @@ -1143,28 +1146,36 @@ describe('Timeline profiler', () => { }); describe('DevTools hook (in memory)', () => { + let getBatchOfWork; + let stopProfilingAndGetTimelineData; + beforeEach(() => { - utils = require('./utils'); - utils.beforeEachProfiling(); - - unmountFns = []; - renderHelper = element => { - const unmountFn = utils.legacyRender(element); - unmountFns.push(unmountFn); - return unmountFn; - }; - renderRootHelper = element => { - const container = document.createElement('div'); - const root = ReactDOM.createRoot(container); - root.render(element); - const unmountFn = () => root.unmount(); - unmountFns.push(unmountFn); - return unmountFn; + getBatchOfWork = index => { + const timelineData = stopProfilingAndGetTimelineData(); + if (timelineData) { + if (timelineData.batchUIDToMeasuresMap.size > index) { + return Array.from(timelineData.batchUIDToMeasuresMap.values())[ + index + ]; + } + } + + return null; }; - React = require('react'); - ReactDOM = require('react-dom'); - Scheduler = require('scheduler'); + stopProfilingAndGetTimelineData = () => { + utils.act(() => store.profilerStore.stopProfiling()); + + const timelineData = store.profilerStore.profilingData?.timelineData; + + if (timelineData) { + expect(timelineData).toHaveLength(1); + + return timelineData[0]; + } else { + return null; + } + }; }); afterEach(() => { diff --git a/packages/react-devtools-shared/src/__tests__/__serializers__/profilingSerializer.js b/packages/react-devtools-shared/src/__tests__/__serializers__/profilingSerializer.js new file mode 100644 index 0000000000000..be988e22b2263 --- /dev/null +++ b/packages/react-devtools-shared/src/__tests__/__serializers__/profilingSerializer.js @@ -0,0 +1,25 @@ +import hasOwnProperty from 'shared/hasOwnProperty'; + +const FILTERED_VERSION_STRING = ''; + +// test() is part of Jest's serializer API +export function test(maybeProfile) { + if ( + maybeProfile != null && + typeof maybeProfile === 'object' && + hasOwnProperty.call(maybeProfile, 'reactVersion') && + maybeProfile.reactVersion !== FILTERED_VERSION_STRING + ) { + return true; + } + + return false; +} + +// print() is part of Jest's serializer API +export function print(profile, serialize, indent) { + return serialize({ + ...profile, + reactVersion: FILTERED_VERSION_STRING, + }); +} diff --git a/packages/react-devtools-shared/src/__tests__/__serializers__/timelineDataSerializer.js b/packages/react-devtools-shared/src/__tests__/__serializers__/timelineDataSerializer.js new file mode 100644 index 0000000000000..1ece2010eec02 --- /dev/null +++ b/packages/react-devtools-shared/src/__tests__/__serializers__/timelineDataSerializer.js @@ -0,0 +1,29 @@ +import hasOwnProperty from 'shared/hasOwnProperty'; +import isArray from 'shared/isArray'; + +function formatLanes(laneArray) { + const lanes = laneArray.reduce((current, reduced) => current + reduced, 0); + return '0b' + lanes.toString(2).padStart(31, '0'); +} + +// test() is part of Jest's serializer API +export function test(maybeTimelineData) { + if ( + maybeTimelineData != null && + typeof maybeTimelineData === 'object' && + hasOwnProperty.call(maybeTimelineData, 'lanes') && + isArray(maybeTimelineData.lanes) + ) { + return true; + } + + return false; +} + +// print() is part of Jest's serializer API +export function print(timelineData, serialize, indent) { + return serialize({ + ...timelineData, + lanes: formatLanes(timelineData.lanes), + }); +} diff --git a/packages/react-devtools-shared/src/__tests__/__snapshots__/profilingCache-test.js.snap b/packages/react-devtools-shared/src/__tests__/__snapshots__/profilingCache-test.js.snap index 450d176f0ac55..82e48a5e5d3d3 100644 --- a/packages/react-devtools-shared/src/__tests__/__snapshots__/profilingCache-test.js.snap +++ b/packages/react-devtools-shared/src/__tests__/__snapshots__/profilingCache-test.js.snap @@ -836,907 +836,2536 @@ Object { "snapshots": Array [], }, ], - "version": 5, -} -`; - -exports[`ProfilingCache should collect data for each rendered fiber: FiberCommits: element 2 1`] = ` -Array [ - 0, - 1, - 2, -] -`; - -exports[`ProfilingCache should collect data for each rendered fiber: FiberCommits: element 4 1`] = ` -Array [ - 0, - 1, - 2, -] -`; - -exports[`ProfilingCache should collect data for each rendered fiber: FiberCommits: element 5 1`] = ` -Array [ - 0, -] -`; - -exports[`ProfilingCache should collect data for each rendered fiber: FiberCommits: element 6 1`] = ` -Array [ - 1, - 2, -] -`; - -exports[`ProfilingCache should collect data for each rendered fiber: FiberCommits: element 7 1`] = ` -Array [ - 2, -] -`; - -exports[`ProfilingCache should collect data for each rendered fiber: imported data 1`] = ` -Object { - "dataForRoots": Array [ + "timelineData": Array [ Object { - "commitData": Array [ - Object { - "changeDescriptions": Array [ - Array [ - 2, - Object { - "context": null, - "didHooksChange": false, - "isFirstMount": true, - "props": null, - "state": null, - }, - ], - Array [ - 4, - Object { - "context": null, - "didHooksChange": false, - "isFirstMount": true, - "props": null, - "state": null, - }, - ], - Array [ - 5, - Object { - "context": null, - "didHooksChange": false, - "isFirstMount": true, - "props": null, - "state": null, - }, - ], - ], - "duration": 11, - "effectDuration": null, - "fiberActualDurations": Array [ - Array [ - 1, - 11, - ], - Array [ - 2, - 11, - ], - Array [ - 4, - 0, - ], - Array [ - 5, - 1, - ], - ], - "fiberSelfDurations": Array [ - Array [ - 1, - 0, - ], - Array [ - 2, - 10, - ], - Array [ - 4, - 0, - ], - Array [ - 5, - 1, - ], - ], - "passiveEffectDuration": null, - "priorityLevel": "Immediate", - "timestamp": 11, - "updaters": Array [ + "batchUIDToMeasuresMap": Array [ + Array [ + 1, + Array [ Object { - "displayName": "render()", - "hocDisplayNames": null, - "id": 1, - "key": null, - "type": 11, + "batchUID": 1, + "depth": 0, + "duration": 12, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 12, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 22, + "type": "commit", + }, + Object { + "batchUID": 1, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 22, + "type": "layout-effects", }, ], - }, - Object { - "changeDescriptions": Array [ - Array [ - 4, - Object { - "context": null, - "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [], - "state": null, - }, - ], - Array [ - 6, - Object { - "context": null, - "didHooksChange": false, - "isFirstMount": true, - "props": null, - "state": null, - }, - ], - Array [ - 2, - Object { - "context": null, - "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [ - "count", - ], - "state": null, - }, - ], - ], - "duration": 11, - "effectDuration": null, - "fiberActualDurations": Array [ - Array [ - 4, - 0, - ], - Array [ - 6, - 1, - ], - Array [ - 2, - 11, - ], - Array [ - 1, - 11, - ], + ], + Array [ + 2, + Array [ + Object { + "batchUID": 2, + "depth": 0, + "duration": 13, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 22, + "type": "render-idle", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 13, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 22, + "type": "render", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 35, + "type": "commit", + }, + Object { + "batchUID": 2, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 35, + "type": "layout-effects", + }, ], - "fiberSelfDurations": Array [ - Array [ - 4, - 0, - ], - Array [ - 6, - 1, - ], - Array [ - 2, - 10, - ], - Array [ - 1, - 0, - ], + ], + Array [ + 3, + Array [ + Object { + "batchUID": 3, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 35, + "type": "render-idle", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 35, + "type": "render", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 45, + "type": "commit", + }, + Object { + "batchUID": 3, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 45, + "type": "layout-effects", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 45, + "type": "passive-effects", + }, ], - "passiveEffectDuration": null, - "priorityLevel": "Immediate", - "timestamp": 22, - "updaters": Array [ + ], + Array [ + 4, + Array [ Object { - "displayName": "render()", - "hocDisplayNames": null, - "id": 1, - "key": null, - "type": 11, + "batchUID": 4, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 45, + "type": "render-idle", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 45, + "type": "render", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 55, + "type": "commit", + }, + Object { + "batchUID": 4, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 55, + "type": "layout-effects", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 55, + "type": "passive-effects", }, ], + ], + ], + "componentMeasures": Array [ + Object { + "componentName": "Parent", + "duration": 10, + "timestamp": 10, + "type": "render", + "warning": null, }, Object { - "changeDescriptions": Array [ - Array [ - 4, - Object { - "context": null, - "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [], - "state": null, - }, - ], - Array [ - 6, - Object { - "context": null, - "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [], - "state": null, - }, - ], - Array [ - 7, - Object { - "context": null, - "didHooksChange": false, - "isFirstMount": true, - "props": null, - "state": null, - }, - ], - Array [ - 2, - Object { - "context": null, - "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [ - "count", - ], - "state": null, - }, - ], - ], - "duration": 13, - "effectDuration": null, - "fiberActualDurations": Array [ - Array [ - 4, - 0, - ], - Array [ - 6, - 1, - ], - Array [ - 7, - 2, - ], - Array [ - 2, - 13, - ], - Array [ - 1, - 13, - ], - ], - "fiberSelfDurations": Array [ - Array [ - 4, - 0, - ], - Array [ - 6, - 1, - ], - Array [ - 7, - 2, - ], - Array [ - 2, - 10, - ], - Array [ - 1, - 0, - ], - ], - "passiveEffectDuration": null, - "priorityLevel": "Immediate", + "componentName": "Child", + "duration": 0, + "timestamp": 20, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 1, + "timestamp": 20, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 1, + "timestamp": 21, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Parent", + "duration": 10, + "timestamp": 22, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 0, + "timestamp": 32, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 1, + "timestamp": 32, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 2, + "timestamp": 33, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Parent", + "duration": 10, "timestamp": 35, - "updaters": Array [ - Object { - "displayName": "render()", - "hocDisplayNames": null, - "id": 1, - "key": null, - "type": 11, - }, - ], + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 0, + "timestamp": 45, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Parent", + "duration": 10, + "timestamp": 45, + "type": "render", + "warning": null, }, ], - "displayName": "Parent", - "initialTreeBaseDurations": Array [], - "operations": Array [ + "duration": 65, + "flamechart": Array [], + "internalModuleSourceToRanges": Array [], + "laneToLabelMap": Array [ Array [ 1, - 1, - 15, - 6, - 80, - 97, - 114, - 101, - 110, - 116, - 5, - 67, - 104, - 105, - 108, - 100, - 1, - 48, - 1, - 1, - 11, - 0, - 3, - 1, - 1, - 4, - 1, - 11000, - 1, - 2, - 5, - 1, - 0, - 1, - 0, - 4, - 2, - 11000, - 1, - 4, - 5, - 2, - 2, - 2, - 3, - 4, - 4, - 0, - 1, - 5, - 8, - 2, - 2, - 2, - 0, - 4, - 5, - 1000, + "Sync", ], Array [ - 1, - 1, - 8, - 5, - 67, - 104, - 105, - 108, - 100, - 1, - 49, - 1, - 6, - 5, - 2, - 2, - 1, - 2, - 4, - 6, - 1000, - 4, - 2, - 12000, - 3, 2, - 3, - 4, - 6, - 5, + "InputContinuousHydration", + ], + Array [ 4, - 1, - 12000, + "InputContinuous", ], Array [ - 1, - 1, 8, - 5, - 67, - 104, - 105, - 108, - 100, - 1, - 50, - 1, - 7, - 5, - 2, - 2, - 1, - 2, - 4, - 7, - 2000, - 4, - 2, - 14000, - 3, - 2, - 4, - 4, - 6, - 7, - 5, - 4, - 1, - 14000, + "DefaultHydration", ], - ], - "rootID": 1, + Array [ + 16, + "Default", + ], + Array [ + 32, + "TransitionHydration", + ], + Array [ + 64, + "Transition", + ], + Array [ + 128, + "Transition", + ], + Array [ + 256, + "Transition", + ], + Array [ + 512, + "Transition", + ], + Array [ + 1024, + "Transition", + ], + Array [ + 2048, + "Transition", + ], + Array [ + 4096, + "Transition", + ], + Array [ + 8192, + "Transition", + ], + Array [ + 16384, + "Transition", + ], + Array [ + 32768, + "Transition", + ], + Array [ + 65536, + "Transition", + ], + Array [ + 131072, + "Transition", + ], + Array [ + 262144, + "Transition", + ], + Array [ + 524288, + "Transition", + ], + Array [ + 1048576, + "Transition", + ], + Array [ + 2097152, + "Transition", + ], + Array [ + 4194304, + "Retry", + ], + Array [ + 8388608, + "Retry", + ], + Array [ + 16777216, + "Retry", + ], + Array [ + 33554432, + "Retry", + ], + Array [ + 67108864, + "Retry", + ], + Array [ + 134217728, + "SelectiveHydration", + ], + Array [ + 268435456, + "IdleHydration", + ], + Array [ + 536870912, + "Idle", + ], + Array [ + 1073741824, + "Offscreen", + ], + ], + "laneToReactMeasureMap": Array [ + Array [ + 1, + Array [ + Object { + "batchUID": 1, + "depth": 0, + "duration": 12, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 12, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 22, + "type": "commit", + }, + Object { + "batchUID": 1, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 22, + "type": "layout-effects", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 13, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 22, + "type": "render-idle", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 13, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 22, + "type": "render", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 35, + "type": "commit", + }, + Object { + "batchUID": 2, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 35, + "type": "layout-effects", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 35, + "type": "render-idle", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 35, + "type": "render", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 45, + "type": "commit", + }, + Object { + "batchUID": 3, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 45, + "type": "layout-effects", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 45, + "type": "passive-effects", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 45, + "type": "render-idle", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 45, + "type": "render", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 55, + "type": "commit", + }, + Object { + "batchUID": 4, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 55, + "type": "layout-effects", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 55, + "type": "passive-effects", + }, + ], + ], + Array [ + 2, + Array [], + ], + Array [ + 4, + Array [], + ], + Array [ + 8, + Array [], + ], + Array [ + 16, + Array [], + ], + Array [ + 32, + Array [], + ], + Array [ + 64, + Array [], + ], + Array [ + 128, + Array [], + ], + Array [ + 256, + Array [], + ], + Array [ + 512, + Array [], + ], + Array [ + 1024, + Array [], + ], + Array [ + 2048, + Array [], + ], + Array [ + 4096, + Array [], + ], + Array [ + 8192, + Array [], + ], + Array [ + 16384, + Array [], + ], + Array [ + 32768, + Array [], + ], + Array [ + 65536, + Array [], + ], + Array [ + 131072, + Array [], + ], + Array [ + 262144, + Array [], + ], + Array [ + 524288, + Array [], + ], + Array [ + 1048576, + Array [], + ], + Array [ + 2097152, + Array [], + ], + Array [ + 4194304, + Array [], + ], + Array [ + 8388608, + Array [], + ], + Array [ + 16777216, + Array [], + ], + Array [ + 33554432, + Array [], + ], + Array [ + 67108864, + Array [], + ], + Array [ + 134217728, + Array [], + ], + Array [ + 268435456, + Array [], + ], + Array [ + 536870912, + Array [], + ], + Array [ + 1073741824, + Array [], + ], + ], + "nativeEvents": Array [], + "networkMeasures": Array [], + "otherUserTimingMarks": Array [], + "reactVersion": "", + "schedulingEvents": Array [ + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 22, + "type": "schedule-render", + "warning": null, + }, + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 35, + "type": "schedule-render", + "warning": null, + }, + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 45, + "type": "schedule-render", + "warning": null, + }, + ], + "snapshotHeight": 0, "snapshots": Array [], + "startTime": -10, + "suspenseEvents": Array [], + "thrownErrors": Array [], }, ], "version": 5, } `; -exports[`ProfilingCache should collect data for each root (including ones added or mounted after profiling started): Data for root Parent 1`] = ` +exports[`ProfilingCache should collect data for each rendered fiber: FiberCommits: element 2 1`] = ` +Array [ + 0, + 1, + 2, +] +`; + +exports[`ProfilingCache should collect data for each rendered fiber: FiberCommits: element 4 1`] = ` +Array [ + 0, + 1, + 2, +] +`; + +exports[`ProfilingCache should collect data for each rendered fiber: FiberCommits: element 5 1`] = ` +Array [ + 0, +] +`; + +exports[`ProfilingCache should collect data for each rendered fiber: FiberCommits: element 6 1`] = ` +Array [ + 1, + 2, +] +`; + +exports[`ProfilingCache should collect data for each rendered fiber: FiberCommits: element 7 1`] = ` +Array [ + 2, +] +`; + +exports[`ProfilingCache should collect data for each rendered fiber: imported data 1`] = ` Object { - "commitData": Array [ + "dataForRoots": Array [ Object { - "changeDescriptions": Map { - 4 => Object { - "context": null, - "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [], - "state": null, - }, - 5 => Object { - "context": null, - "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [], - "state": null, - }, - 12 => Object { - "context": null, - "didHooksChange": false, - "isFirstMount": true, - "props": null, - "state": null, - }, - 2 => Object { - "context": null, - "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [ - "count", - ], - "state": null, - }, - }, - "duration": 13, - "effectDuration": null, - "fiberActualDurations": Map { - 4 => 0, - 5 => 1, - 12 => 2, - 2 => 13, - 1 => 13, - }, - "fiberSelfDurations": Map { - 4 => 0, - 5 => 1, - 12 => 2, - 2 => 10, - 1 => 0, - }, - "passiveEffectDuration": null, - "priorityLevel": "Immediate", - "timestamp": 13, - "updaters": Array [ + "commitData": Array [ Object { - "displayName": "render()", - "hocDisplayNames": null, - "id": 1, - "key": null, - "type": 11, - }, - ], - }, - Object { - "changeDescriptions": Map { - 4 => Object { - "context": null, - "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [], - "state": null, - }, - 2 => Object { - "context": null, - "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [ - "count", + "changeDescriptions": Array [ + Array [ + 2, + Object { + "context": null, + "didHooksChange": false, + "isFirstMount": true, + "props": null, + "state": null, + }, + ], + Array [ + 4, + Object { + "context": null, + "didHooksChange": false, + "isFirstMount": true, + "props": null, + "state": null, + }, + ], + Array [ + 5, + Object { + "context": null, + "didHooksChange": false, + "isFirstMount": true, + "props": null, + "state": null, + }, + ], + ], + "duration": 11, + "effectDuration": null, + "fiberActualDurations": Array [ + Array [ + 1, + 11, + ], + Array [ + 2, + 11, + ], + Array [ + 4, + 0, + ], + Array [ + 5, + 1, + ], + ], + "fiberSelfDurations": Array [ + Array [ + 1, + 0, + ], + Array [ + 2, + 10, + ], + Array [ + 4, + 0, + ], + Array [ + 5, + 1, + ], + ], + "passiveEffectDuration": null, + "priorityLevel": "Immediate", + "timestamp": 11, + "updaters": Array [ + Object { + "displayName": "render()", + "hocDisplayNames": null, + "id": 1, + "key": null, + "type": 11, + }, ], - "state": null, }, - }, - "duration": 10, - "effectDuration": 0, - "fiberActualDurations": Map { - 4 => 0, - 2 => 10, - 1 => 10, - }, - "fiberSelfDurations": Map { - 4 => 0, - 2 => 10, - 1 => 0, - }, - "passiveEffectDuration": 0, - "priorityLevel": "Immediate", - "timestamp": 34, - "updaters": Array [ Object { - "displayName": "render()", - "hocDisplayNames": null, - "id": 1, - "key": null, - "type": 11, - }, - ], - }, - Object { - "changeDescriptions": Map { - 2 => Object { - "context": null, - "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [ - "count", + "changeDescriptions": Array [ + Array [ + 4, + Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [], + "state": null, + }, + ], + Array [ + 6, + Object { + "context": null, + "didHooksChange": false, + "isFirstMount": true, + "props": null, + "state": null, + }, + ], + Array [ + 2, + Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [ + "count", + ], + "state": null, + }, + ], + ], + "duration": 11, + "effectDuration": null, + "fiberActualDurations": Array [ + Array [ + 4, + 0, + ], + Array [ + 6, + 1, + ], + Array [ + 2, + 11, + ], + Array [ + 1, + 11, + ], + ], + "fiberSelfDurations": Array [ + Array [ + 4, + 0, + ], + Array [ + 6, + 1, + ], + Array [ + 2, + 10, + ], + Array [ + 1, + 0, + ], + ], + "passiveEffectDuration": null, + "priorityLevel": "Immediate", + "timestamp": 22, + "updaters": Array [ + Object { + "displayName": "render()", + "hocDisplayNames": null, + "id": 1, + "key": null, + "type": 11, + }, ], - "state": null, }, - }, - "duration": 10, - "effectDuration": 0, - "fiberActualDurations": Map { - 2 => 10, - 1 => 10, - }, - "fiberSelfDurations": Map { - 2 => 10, - 1 => 0, - }, - "passiveEffectDuration": 0, - "priorityLevel": "Immediate", - "timestamp": 44, - "updaters": Array [ Object { - "displayName": "render()", - "hocDisplayNames": null, - "id": 1, - "key": null, - "type": 11, + "changeDescriptions": Array [ + Array [ + 4, + Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [], + "state": null, + }, + ], + Array [ + 6, + Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [], + "state": null, + }, + ], + Array [ + 7, + Object { + "context": null, + "didHooksChange": false, + "isFirstMount": true, + "props": null, + "state": null, + }, + ], + Array [ + 2, + Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [ + "count", + ], + "state": null, + }, + ], + ], + "duration": 13, + "effectDuration": null, + "fiberActualDurations": Array [ + Array [ + 4, + 0, + ], + Array [ + 6, + 1, + ], + Array [ + 7, + 2, + ], + Array [ + 2, + 13, + ], + Array [ + 1, + 13, + ], + ], + "fiberSelfDurations": Array [ + Array [ + 4, + 0, + ], + Array [ + 6, + 1, + ], + Array [ + 7, + 2, + ], + Array [ + 2, + 10, + ], + Array [ + 1, + 0, + ], + ], + "passiveEffectDuration": null, + "priorityLevel": "Immediate", + "timestamp": 35, + "updaters": Array [ + Object { + "displayName": "render()", + "hocDisplayNames": null, + "id": 1, + "key": null, + "type": 11, + }, + ], }, ], + "displayName": "Parent", + "initialTreeBaseDurations": Array [], + "operations": Array [ + Array [ + 1, + 1, + 15, + 6, + 80, + 97, + 114, + 101, + 110, + 116, + 5, + 67, + 104, + 105, + 108, + 100, + 1, + 48, + 1, + 1, + 11, + 0, + 3, + 1, + 1, + 4, + 1, + 11000, + 1, + 2, + 5, + 1, + 0, + 1, + 0, + 4, + 2, + 11000, + 1, + 4, + 5, + 2, + 2, + 2, + 3, + 4, + 4, + 0, + 1, + 5, + 8, + 2, + 2, + 2, + 0, + 4, + 5, + 1000, + ], + Array [ + 1, + 1, + 8, + 5, + 67, + 104, + 105, + 108, + 100, + 1, + 49, + 1, + 6, + 5, + 2, + 2, + 1, + 2, + 4, + 6, + 1000, + 4, + 2, + 12000, + 3, + 2, + 3, + 4, + 6, + 5, + 4, + 1, + 12000, + ], + Array [ + 1, + 1, + 8, + 5, + 67, + 104, + 105, + 108, + 100, + 1, + 50, + 1, + 7, + 5, + 2, + 2, + 1, + 2, + 4, + 7, + 2000, + 4, + 2, + 14000, + 3, + 2, + 4, + 4, + 6, + 7, + 5, + 4, + 1, + 14000, + ], + ], + "rootID": 1, + "snapshots": Array [], }, ], - "displayName": "Parent", - "initialTreeBaseDurations": Map { - 1 => 12, - 2 => 12, - 4 => 0, - 5 => 1, - 6 => 1, - }, - "operations": Array [ - Array [ - 1, - 1, - 8, - 5, - 67, - 104, - 105, - 108, - 100, - 1, - 50, - 1, - 12, - 5, - 2, - 2, - 1, - 2, - 4, - 12, - 2000, - 4, - 2, - 14000, - 3, - 2, - 4, - 4, - 5, - 12, - 6, - 4, - 1, - 14000, - ], - Array [ - 1, - 1, - 0, - 2, - 2, - 12, - 5, - 4, - 2, - 11000, - 3, - 2, - 2, - 4, - 6, - 4, - 1, - 11000, - ], - Array [ - 1, - 1, - 0, - 2, - 1, - 4, - ], - ], - "rootID": 1, - "snapshots": Map { - 1 => Object { - "children": Array [ - 2, - ], - "displayName": null, - "hocDisplayNames": null, - "id": 1, - "key": null, - "type": 11, - }, - 2 => Object { - "children": Array [ - 4, - 5, - 6, - ], - "displayName": "Parent", - "hocDisplayNames": null, - "id": 2, - "key": null, - "type": 5, - }, - 4 => Object { - "children": Array [], - "displayName": "Child", - "hocDisplayNames": null, - "id": 4, - "key": "0", - "type": 5, - }, - 5 => Object { - "children": Array [], - "displayName": "Child", - "hocDisplayNames": null, - "id": 5, - "key": "1", - "type": 5, - }, - 6 => Object { - "children": Array [], - "displayName": "Child", - "hocDisplayNames": Array [ - "Memo", - ], - "id": 6, - "key": null, - "type": 8, - }, - }, -} -`; - -exports[`ProfilingCache should collect data for each root (including ones added or mounted after profiling started): Data for root Parent 2`] = ` -Object { - "commitData": Array [ + "timelineData": Array [ Object { - "changeDescriptions": Map { - 14 => Object { - "context": null, - "didHooksChange": false, - "isFirstMount": true, - "props": null, - "state": null, + "batchUIDToMeasuresMap": Array [ + Array [ + 1, + Array [ + Object { + "batchUID": 1, + "depth": 0, + "duration": 11, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 11, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 21, + "type": "commit", + }, + Object { + "batchUID": 1, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 21, + "type": "layout-effects", + }, + ], + ], + Array [ + 2, + Array [ + Object { + "batchUID": 2, + "depth": 0, + "duration": 11, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 21, + "type": "render-idle", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 11, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 21, + "type": "render", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 32, + "type": "commit", + }, + Object { + "batchUID": 2, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 32, + "type": "layout-effects", + }, + ], + ], + Array [ + 3, + Array [ + Object { + "batchUID": 3, + "depth": 0, + "duration": 13, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 32, + "type": "render-idle", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 13, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 32, + "type": "render", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 45, + "type": "commit", + }, + Object { + "batchUID": 3, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 45, + "type": "layout-effects", + }, + ], + ], + ], + "componentMeasures": Array [ + Object { + "componentName": "Parent", + "duration": 10, + "timestamp": 10, + "type": "render", + "warning": null, }, - 16 => Object { - "context": null, - "didHooksChange": false, - "isFirstMount": true, - "props": null, - "state": null, + Object { + "componentName": "Child", + "duration": 0, + "timestamp": 20, + "type": "render", + "warning": null, }, - 17 => Object { - "context": null, - "didHooksChange": false, - "isFirstMount": true, - "props": null, - "state": null, + Object { + "componentName": "Child", + "duration": 1, + "timestamp": 20, + "type": "render", + "warning": null, }, - }, - "duration": 11, - "effectDuration": null, - "fiberActualDurations": Map { - 13 => 11, - 14 => 11, - 16 => 0, - 17 => 1, - }, - "fiberSelfDurations": Map { - 13 => 0, - 14 => 10, - 16 => 0, - 17 => 1, - }, - "passiveEffectDuration": null, - "priorityLevel": "Immediate", - "timestamp": 24, - "updaters": Array [ Object { - "displayName": "render()", - "hocDisplayNames": null, - "id": 13, - "key": null, - "type": 11, + "componentName": "Parent", + "duration": 10, + "timestamp": 21, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 0, + "timestamp": 31, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 1, + "timestamp": 31, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Parent", + "duration": 10, + "timestamp": 32, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 0, + "timestamp": 42, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 1, + "timestamp": 42, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 2, + "timestamp": 43, + "type": "render", + "warning": null, }, ], - }, - ], - "displayName": "Parent", - "initialTreeBaseDurations": Map {}, - "operations": Array [ - Array [ - 1, - 13, - 15, - 6, - 80, - 97, - 114, - 101, - 110, - 116, - 5, - 67, - 104, - 105, - 108, - 100, - 1, - 48, - 1, - 13, - 11, - 0, - 3, - 1, - 1, - 4, - 13, - 11000, - 1, - 14, - 5, - 13, - 0, - 1, - 0, - 4, - 14, - 11000, - 1, - 16, - 5, - 14, - 14, - 2, - 3, - 4, - 16, - 0, - 1, - 17, - 8, - 14, - 14, - 2, - 0, - 4, - 17, - 1000, - ], + "duration": 55, + "flamechart": Array [], + "internalModuleSourceToRanges": Array [], + "laneToLabelMap": Array [ + Array [ + 1, + "Sync", + ], + Array [ + 2, + "InputContinuousHydration", + ], + Array [ + 4, + "InputContinuous", + ], + Array [ + 8, + "DefaultHydration", + ], + Array [ + 16, + "Default", + ], + Array [ + 32, + "TransitionHydration", + ], + Array [ + 64, + "Transition", + ], + Array [ + 128, + "Transition", + ], + Array [ + 256, + "Transition", + ], + Array [ + 512, + "Transition", + ], + Array [ + 1024, + "Transition", + ], + Array [ + 2048, + "Transition", + ], + Array [ + 4096, + "Transition", + ], + Array [ + 8192, + "Transition", + ], + Array [ + 16384, + "Transition", + ], + Array [ + 32768, + "Transition", + ], + Array [ + 65536, + "Transition", + ], + Array [ + 131072, + "Transition", + ], + Array [ + 262144, + "Transition", + ], + Array [ + 524288, + "Transition", + ], + Array [ + 1048576, + "Transition", + ], + Array [ + 2097152, + "Transition", + ], + Array [ + 4194304, + "Retry", + ], + Array [ + 8388608, + "Retry", + ], + Array [ + 16777216, + "Retry", + ], + Array [ + 33554432, + "Retry", + ], + Array [ + 67108864, + "Retry", + ], + Array [ + 134217728, + "SelectiveHydration", + ], + Array [ + 268435456, + "IdleHydration", + ], + Array [ + 536870912, + "Idle", + ], + Array [ + 1073741824, + "Offscreen", + ], + ], + "laneToReactMeasureMap": Array [ + Array [ + 1, + Array [ + Object { + "batchUID": 1, + "depth": 0, + "duration": 11, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 11, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 21, + "type": "commit", + }, + Object { + "batchUID": 1, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 21, + "type": "layout-effects", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 11, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 21, + "type": "render-idle", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 11, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 21, + "type": "render", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 32, + "type": "commit", + }, + Object { + "batchUID": 2, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 32, + "type": "layout-effects", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 13, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 32, + "type": "render-idle", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 13, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 32, + "type": "render", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 45, + "type": "commit", + }, + Object { + "batchUID": 3, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 45, + "type": "layout-effects", + }, + ], + ], + Array [ + 2, + Array [], + ], + Array [ + 4, + Array [], + ], + Array [ + 8, + Array [], + ], + Array [ + 16, + Array [], + ], + Array [ + 32, + Array [], + ], + Array [ + 64, + Array [], + ], + Array [ + 128, + Array [], + ], + Array [ + 256, + Array [], + ], + Array [ + 512, + Array [], + ], + Array [ + 1024, + Array [], + ], + Array [ + 2048, + Array [], + ], + Array [ + 4096, + Array [], + ], + Array [ + 8192, + Array [], + ], + Array [ + 16384, + Array [], + ], + Array [ + 32768, + Array [], + ], + Array [ + 65536, + Array [], + ], + Array [ + 131072, + Array [], + ], + Array [ + 262144, + Array [], + ], + Array [ + 524288, + Array [], + ], + Array [ + 1048576, + Array [], + ], + Array [ + 2097152, + Array [], + ], + Array [ + 4194304, + Array [], + ], + Array [ + 8388608, + Array [], + ], + Array [ + 16777216, + Array [], + ], + Array [ + 33554432, + Array [], + ], + Array [ + 67108864, + Array [], + ], + Array [ + 134217728, + Array [], + ], + Array [ + 268435456, + Array [], + ], + Array [ + 536870912, + Array [], + ], + Array [ + 1073741824, + Array [], + ], + ], + "nativeEvents": Array [], + "networkMeasures": Array [], + "otherUserTimingMarks": Array [], + "reactVersion": "", + "schedulingEvents": Array [ + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 21, + "type": "schedule-render", + "warning": null, + }, + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 32, + "type": "schedule-render", + "warning": null, + }, + ], + "snapshotHeight": 0, + "snapshots": Array [], + "startTime": -10, + "suspenseEvents": Array [], + "thrownErrors": Array [], + }, ], - "rootID": 13, - "snapshots": Map {}, + "version": 5, } `; -exports[`ProfilingCache should collect data for each root (including ones added or mounted after profiling started): imported data 1`] = ` +exports[`ProfilingCache should collect data for each root (including ones added or mounted after profiling started): Data for root Parent 1`] = ` Object { - "dataForRoots": Array [ + "commitData": Array [ + Object { + "changeDescriptions": Map { + 4 => Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [], + "state": null, + }, + 5 => Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [], + "state": null, + }, + 12 => Object { + "context": null, + "didHooksChange": false, + "isFirstMount": true, + "props": null, + "state": null, + }, + 2 => Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [ + "count", + ], + "state": null, + }, + }, + "duration": 13, + "effectDuration": null, + "fiberActualDurations": Map { + 4 => 0, + 5 => 1, + 12 => 2, + 2 => 13, + 1 => 13, + }, + "fiberSelfDurations": Map { + 4 => 0, + 5 => 1, + 12 => 2, + 2 => 10, + 1 => 0, + }, + "passiveEffectDuration": null, + "priorityLevel": "Immediate", + "timestamp": 13, + "updaters": Array [ + Object { + "displayName": "render()", + "hocDisplayNames": null, + "id": 1, + "key": null, + "type": 11, + }, + ], + }, + Object { + "changeDescriptions": Map { + 4 => Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [], + "state": null, + }, + 2 => Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [ + "count", + ], + "state": null, + }, + }, + "duration": 10, + "effectDuration": 0, + "fiberActualDurations": Map { + 4 => 0, + 2 => 10, + 1 => 10, + }, + "fiberSelfDurations": Map { + 4 => 0, + 2 => 10, + 1 => 0, + }, + "passiveEffectDuration": 0, + "priorityLevel": "Immediate", + "timestamp": 34, + "updaters": Array [ + Object { + "displayName": "render()", + "hocDisplayNames": null, + "id": 1, + "key": null, + "type": 11, + }, + ], + }, + Object { + "changeDescriptions": Map { + 2 => Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [ + "count", + ], + "state": null, + }, + }, + "duration": 10, + "effectDuration": 0, + "fiberActualDurations": Map { + 2 => 10, + 1 => 10, + }, + "fiberSelfDurations": Map { + 2 => 10, + 1 => 0, + }, + "passiveEffectDuration": 0, + "priorityLevel": "Immediate", + "timestamp": 44, + "updaters": Array [ + Object { + "displayName": "render()", + "hocDisplayNames": null, + "id": 1, + "key": null, + "type": 11, + }, + ], + }, + ], + "displayName": "Parent", + "initialTreeBaseDurations": Map { + 1 => 12, + 2 => 12, + 4 => 0, + 5 => 1, + 6 => 1, + }, + "operations": Array [ + Array [ + 1, + 1, + 8, + 5, + 67, + 104, + 105, + 108, + 100, + 1, + 50, + 1, + 12, + 5, + 2, + 2, + 1, + 2, + 4, + 12, + 2000, + 4, + 2, + 14000, + 3, + 2, + 4, + 4, + 5, + 12, + 6, + 4, + 1, + 14000, + ], + Array [ + 1, + 1, + 0, + 2, + 2, + 12, + 5, + 4, + 2, + 11000, + 3, + 2, + 2, + 4, + 6, + 4, + 1, + 11000, + ], + Array [ + 1, + 1, + 0, + 2, + 1, + 4, + ], + ], + "rootID": 1, + "snapshots": Map { + 1 => Object { + "children": Array [ + 2, + ], + "displayName": null, + "hocDisplayNames": null, + "id": 1, + "key": null, + "type": 11, + }, + 2 => Object { + "children": Array [ + 4, + 5, + 6, + ], + "displayName": "Parent", + "hocDisplayNames": null, + "id": 2, + "key": null, + "type": 5, + }, + 4 => Object { + "children": Array [], + "displayName": "Child", + "hocDisplayNames": null, + "id": 4, + "key": "0", + "type": 5, + }, + 5 => Object { + "children": Array [], + "displayName": "Child", + "hocDisplayNames": null, + "id": 5, + "key": "1", + "type": 5, + }, + 6 => Object { + "children": Array [], + "displayName": "Child", + "hocDisplayNames": Array [ + "Memo", + ], + "id": 6, + "key": null, + "type": 8, + }, + }, +} +`; + +exports[`ProfilingCache should collect data for each root (including ones added or mounted after profiling started): Data for root Parent 2`] = ` +Object { + "commitData": Array [ + Object { + "changeDescriptions": Map { + 14 => Object { + "context": null, + "didHooksChange": false, + "isFirstMount": true, + "props": null, + "state": null, + }, + 16 => Object { + "context": null, + "didHooksChange": false, + "isFirstMount": true, + "props": null, + "state": null, + }, + 17 => Object { + "context": null, + "didHooksChange": false, + "isFirstMount": true, + "props": null, + "state": null, + }, + }, + "duration": 11, + "effectDuration": null, + "fiberActualDurations": Map { + 13 => 11, + 14 => 11, + 16 => 0, + 17 => 1, + }, + "fiberSelfDurations": Map { + 13 => 0, + 14 => 10, + 16 => 0, + 17 => 1, + }, + "passiveEffectDuration": null, + "priorityLevel": "Immediate", + "timestamp": 24, + "updaters": Array [ + Object { + "displayName": "render()", + "hocDisplayNames": null, + "id": 13, + "key": null, + "type": 11, + }, + ], + }, + ], + "displayName": "Parent", + "initialTreeBaseDurations": Map {}, + "operations": Array [ + Array [ + 1, + 13, + 15, + 6, + 80, + 97, + 114, + 101, + 110, + 116, + 5, + 67, + 104, + 105, + 108, + 100, + 1, + 48, + 1, + 13, + 11, + 0, + 3, + 1, + 1, + 4, + 13, + 11000, + 1, + 14, + 5, + 13, + 0, + 1, + 0, + 4, + 14, + 11000, + 1, + 16, + 5, + 14, + 14, + 2, + 3, + 4, + 16, + 0, + 1, + 17, + 8, + 14, + 14, + 2, + 0, + 4, + 17, + 1000, + ], + ], + "rootID": 13, + "snapshots": Map {}, +} +`; + +exports[`ProfilingCache should collect data for each root (including ones added or mounted after profiling started): imported data 1`] = ` +Object { + "dataForRoots": Array [ + Object { + "commitData": Array [ + Object { + "changeDescriptions": Array [ + Array [ + 4, + Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [], + "state": null, + }, + ], + Array [ + 5, + Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [], + "state": null, + }, + ], + Array [ + 12, + Object { + "context": null, + "didHooksChange": false, + "isFirstMount": true, + "props": null, + "state": null, + }, + ], + Array [ + 2, + Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [ + "count", + ], + "state": null, + }, + ], + ], + "duration": 13, + "effectDuration": null, + "fiberActualDurations": Array [ + Array [ + 4, + 0, + ], + Array [ + 5, + 1, + ], + Array [ + 12, + 2, + ], + Array [ + 2, + 13, + ], + Array [ + 1, + 13, + ], + ], + "fiberSelfDurations": Array [ + Array [ + 4, + 0, + ], + Array [ + 5, + 1, + ], + Array [ + 12, + 2, + ], + Array [ + 2, + 10, + ], + Array [ + 1, + 0, + ], + ], + "passiveEffectDuration": null, + "priorityLevel": "Immediate", + "timestamp": 13, + "updaters": Array [ + Object { + "displayName": "render()", + "hocDisplayNames": null, + "id": 1, + "key": null, + "type": 11, + }, + ], + }, + Object { + "changeDescriptions": Array [ + Array [ + 4, + Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [], + "state": null, + }, + ], + Array [ + 2, + Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [ + "count", + ], + "state": null, + }, + ], + ], + "duration": 10, + "effectDuration": 0, + "fiberActualDurations": Array [ + Array [ + 4, + 0, + ], + Array [ + 2, + 10, + ], + Array [ + 1, + 10, + ], + ], + "fiberSelfDurations": Array [ + Array [ + 4, + 0, + ], + Array [ + 2, + 10, + ], + Array [ + 1, + 0, + ], + ], + "passiveEffectDuration": 0, + "priorityLevel": "Immediate", + "timestamp": 34, + "updaters": Array [ + Object { + "displayName": "render()", + "hocDisplayNames": null, + "id": 1, + "key": null, + "type": 11, + }, + ], + }, + Object { + "changeDescriptions": Array [ + Array [ + 2, + Object { + "context": null, + "didHooksChange": false, + "hooks": null, + "isFirstMount": false, + "props": Array [ + "count", + ], + "state": null, + }, + ], + ], + "duration": 10, + "effectDuration": 0, + "fiberActualDurations": Array [ + Array [ + 2, + 10, + ], + Array [ + 1, + 10, + ], + ], + "fiberSelfDurations": Array [ + Array [ + 2, + 10, + ], + Array [ + 1, + 0, + ], + ], + "passiveEffectDuration": 0, + "priorityLevel": "Immediate", + "timestamp": 44, + "updaters": Array [ + Object { + "displayName": "render()", + "hocDisplayNames": null, + "id": 1, + "key": null, + "type": 11, + }, + ], + }, + ], + "displayName": "Parent", + "initialTreeBaseDurations": Array [ + Array [ + 1, + 12, + ], + Array [ + 2, + 12, + ], + Array [ + 4, + 0, + ], + Array [ + 5, + 1, + ], + Array [ + 6, + 1, + ], + ], + "operations": Array [ + Array [ + 1, + 1, + 8, + 5, + 67, + 104, + 105, + 108, + 100, + 1, + 50, + 1, + 12, + 5, + 2, + 2, + 1, + 2, + 4, + 12, + 2000, + 4, + 2, + 14000, + 3, + 2, + 4, + 4, + 5, + 12, + 6, + 4, + 1, + 14000, + ], + Array [ + 1, + 1, + 0, + 2, + 2, + 12, + 5, + 4, + 2, + 11000, + 3, + 2, + 2, + 4, + 6, + 4, + 1, + 11000, + ], + Array [ + 1, + 1, + 0, + 2, + 1, + 4, + ], + ], + "rootID": 1, + "snapshots": Array [ + Array [ + 1, + Object { + "children": Array [ + 2, + ], + "displayName": null, + "hocDisplayNames": null, + "id": 1, + "key": null, + "type": 11, + }, + ], + Array [ + 2, + Object { + "children": Array [ + 4, + 5, + 6, + ], + "displayName": "Parent", + "hocDisplayNames": null, + "id": 2, + "key": null, + "type": 5, + }, + ], + Array [ + 4, + Object { + "children": Array [], + "displayName": "Child", + "hocDisplayNames": null, + "id": 4, + "key": "0", + "type": 5, + }, + ], + Array [ + 5, + Object { + "children": Array [], + "displayName": "Child", + "hocDisplayNames": null, + "id": 5, + "key": "1", + "type": 5, + }, + ], + Array [ + 6, + Object { + "children": Array [], + "displayName": "Child", + "hocDisplayNames": Array [ + "Memo", + ], + "id": 6, + "key": null, + "type": 8, + }, + ], + ], + }, Object { "commitData": Array [ Object { "changeDescriptions": Array [ Array [ - 4, - Object { - "context": null, - "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [], - "state": null, - }, - ], - Array [ - 5, + 14, Object { "context": null, "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [], + "isFirstMount": true, + "props": null, "state": null, }, ], Array [ - 12, + 16, Object { "context": null, "didHooksChange": false, @@ -1746,509 +3375,902 @@ Object { }, ], Array [ - 2, + 17, Object { "context": null, "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [ - "count", - ], + "isFirstMount": true, + "props": null, "state": null, }, ], ], - "duration": 13, + "duration": 11, "effectDuration": null, "fiberActualDurations": Array [ Array [ - 4, - 0, - ], - Array [ - 5, - 1, + 13, + 11, ], Array [ - 12, - 2, + 14, + 11, ], Array [ - 2, - 13, + 16, + 0, ], Array [ + 17, 1, - 13, ], ], "fiberSelfDurations": Array [ Array [ - 4, + 13, 0, ], Array [ - 5, - 1, - ], - Array [ - 12, - 2, + 14, + 10, ], Array [ - 2, - 10, + 16, + 0, ], Array [ + 17, 1, - 0, ], ], "passiveEffectDuration": null, "priorityLevel": "Immediate", - "timestamp": 13, + "timestamp": 24, "updaters": Array [ Object { "displayName": "render()", "hocDisplayNames": null, - "id": 1, + "id": 13, "key": null, "type": 11, }, ], }, - Object { - "changeDescriptions": Array [ - Array [ - 4, - Object { - "context": null, - "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [], - "state": null, - }, - ], - Array [ - 2, - Object { - "context": null, - "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [ - "count", - ], - "state": null, - }, - ], + ], + "displayName": "Parent", + "initialTreeBaseDurations": Array [], + "operations": Array [ + Array [ + 1, + 13, + 15, + 6, + 80, + 97, + 114, + 101, + 110, + 116, + 5, + 67, + 104, + 105, + 108, + 100, + 1, + 48, + 1, + 13, + 11, + 0, + 3, + 1, + 1, + 4, + 13, + 11000, + 1, + 14, + 5, + 13, + 0, + 1, + 0, + 4, + 14, + 11000, + 1, + 16, + 5, + 14, + 14, + 2, + 3, + 4, + 16, + 0, + 1, + 17, + 8, + 14, + 14, + 2, + 0, + 4, + 17, + 1000, + ], + ], + "rootID": 13, + "snapshots": Array [], + }, + ], + "timelineData": Array [ + Object { + "batchUIDToMeasuresMap": Array [ + Array [ + 1, + Array [ + Object { + "batchUID": 1, + "depth": 0, + "duration": 13, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 13, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 23, + "type": "commit", + }, + Object { + "batchUID": 1, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 23, + "type": "layout-effects", + }, ], - "duration": 10, - "effectDuration": 0, - "fiberActualDurations": Array [ - Array [ - 4, - 0, - ], - Array [ - 2, - 10, - ], - Array [ - 1, - 10, - ], + ], + Array [ + 2, + Array [ + Object { + "batchUID": 2, + "depth": 0, + "duration": 11, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 23, + "type": "render-idle", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 11, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 23, + "type": "render", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 34, + "type": "commit", + }, + Object { + "batchUID": 2, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 34, + "type": "layout-effects", + }, + ], + ], + Array [ + 3, + Array [ + Object { + "batchUID": 3, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 34, + "type": "render-idle", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 34, + "type": "render", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "commit", + }, + Object { + "batchUID": 3, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "layout-effects", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "passive-effects", + }, ], - "fiberSelfDurations": Array [ - Array [ - 4, - 0, - ], - Array [ - 2, - 10, - ], - Array [ - 1, - 0, - ], + ], + Array [ + 4, + Array [ + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "render-idle", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "render", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "commit", + }, + Object { + "batchUID": 4, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "layout-effects", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "passive-effects", + }, ], - "passiveEffectDuration": 0, - "priorityLevel": "Immediate", - "timestamp": 34, - "updaters": Array [ + ], + Array [ + 5, + Array [ Object { - "displayName": "render()", - "hocDisplayNames": null, - "id": 1, - "key": null, - "type": 11, + "batchUID": 5, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "render-idle", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "render", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 54, + "type": "commit", + }, + Object { + "batchUID": 5, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 54, + "type": "layout-effects", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 54, + "type": "passive-effects", }, ], + ], + ], + "componentMeasures": Array [ + Object { + "componentName": "Parent", + "duration": 10, + "timestamp": 10, + "type": "render", + "warning": null, }, Object { - "changeDescriptions": Array [ - Array [ - 2, - Object { - "context": null, - "didHooksChange": false, - "hooks": null, - "isFirstMount": false, - "props": Array [ - "count", - ], - "state": null, - }, - ], - ], + "componentName": "Child", + "duration": 0, + "timestamp": 20, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 1, + "timestamp": 20, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 2, + "timestamp": 21, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Parent", "duration": 10, - "effectDuration": 0, - "fiberActualDurations": Array [ - Array [ - 2, - 10, - ], - Array [ - 1, - 10, - ], - ], - "fiberSelfDurations": Array [ - Array [ - 2, - 10, - ], - Array [ - 1, - 0, - ], - ], - "passiveEffectDuration": 0, - "priorityLevel": "Immediate", + "timestamp": 23, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 0, + "timestamp": 33, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 1, + "timestamp": 33, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Parent", + "duration": 10, + "timestamp": 34, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Child", + "duration": 0, "timestamp": 44, - "updaters": Array [ - Object { - "displayName": "render()", - "hocDisplayNames": null, - "id": 1, - "key": null, - "type": 11, - }, - ], + "type": "render", + "warning": null, + }, + Object { + "componentName": "Parent", + "duration": 10, + "timestamp": 44, + "type": "render", + "warning": null, }, ], - "displayName": "Parent", - "initialTreeBaseDurations": Array [ + "duration": 64, + "flamechart": Array [], + "internalModuleSourceToRanges": Array [], + "laneToLabelMap": Array [ Array [ 1, - 12, + "Sync", ], Array [ 2, - 12, + "InputContinuousHydration", ], Array [ 4, - 0, + "InputContinuous", ], Array [ - 5, - 1, + 8, + "DefaultHydration", ], Array [ - 6, - 1, + 16, + "Default", + ], + Array [ + 32, + "TransitionHydration", + ], + Array [ + 64, + "Transition", + ], + Array [ + 128, + "Transition", + ], + Array [ + 256, + "Transition", + ], + Array [ + 512, + "Transition", + ], + Array [ + 1024, + "Transition", + ], + Array [ + 2048, + "Transition", + ], + Array [ + 4096, + "Transition", + ], + Array [ + 8192, + "Transition", + ], + Array [ + 16384, + "Transition", + ], + Array [ + 32768, + "Transition", + ], + Array [ + 65536, + "Transition", + ], + Array [ + 131072, + "Transition", + ], + Array [ + 262144, + "Transition", + ], + Array [ + 524288, + "Transition", + ], + Array [ + 1048576, + "Transition", + ], + Array [ + 2097152, + "Transition", + ], + Array [ + 4194304, + "Retry", + ], + Array [ + 8388608, + "Retry", + ], + Array [ + 16777216, + "Retry", + ], + Array [ + 33554432, + "Retry", + ], + Array [ + 67108864, + "Retry", + ], + Array [ + 134217728, + "SelectiveHydration", + ], + Array [ + 268435456, + "IdleHydration", + ], + Array [ + 536870912, + "Idle", + ], + Array [ + 1073741824, + "Offscreen", ], ], - "operations": Array [ + "laneToReactMeasureMap": Array [ Array [ 1, - 1, - 8, - 5, - 67, - 104, - 105, - 108, - 100, - 1, - 50, - 1, - 12, - 5, - 2, - 2, - 1, - 2, - 4, - 12, - 2000, - 4, - 2, - 14000, - 3, - 2, - 4, - 4, - 5, - 12, - 6, - 4, - 1, - 14000, + Array [ + Object { + "batchUID": 1, + "depth": 0, + "duration": 13, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 13, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 23, + "type": "commit", + }, + Object { + "batchUID": 1, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 23, + "type": "layout-effects", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 11, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 23, + "type": "render-idle", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 11, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 23, + "type": "render", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 34, + "type": "commit", + }, + Object { + "batchUID": 2, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 34, + "type": "layout-effects", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 34, + "type": "render-idle", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 34, + "type": "render", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "commit", + }, + Object { + "batchUID": 3, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "layout-effects", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "passive-effects", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "render-idle", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "render", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "commit", + }, + Object { + "batchUID": 4, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "layout-effects", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "passive-effects", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "render-idle", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 10, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "render", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 54, + "type": "commit", + }, + Object { + "batchUID": 5, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 54, + "type": "layout-effects", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 54, + "type": "passive-effects", + }, + ], ], Array [ - 1, - 1, - 0, - 2, - 2, - 12, - 5, - 4, - 2, - 11000, - 3, - 2, 2, - 4, - 6, - 4, - 1, - 11000, + Array [], ], Array [ - 1, - 1, - 0, - 2, - 1, 4, + Array [], ], - ], - "rootID": 1, - "snapshots": Array [ Array [ - 1, - Object { - "children": Array [ - 2, - ], - "displayName": null, - "hocDisplayNames": null, - "id": 1, - "key": null, - "type": 11, - }, + 8, + Array [], ], Array [ - 2, - Object { - "children": Array [ - 4, - 5, - 6, - ], - "displayName": "Parent", - "hocDisplayNames": null, - "id": 2, - "key": null, - "type": 5, - }, + 16, + Array [], ], Array [ - 4, - Object { - "children": Array [], - "displayName": "Child", - "hocDisplayNames": null, - "id": 4, - "key": "0", - "type": 5, - }, + 32, + Array [], ], Array [ - 5, - Object { - "children": Array [], - "displayName": "Child", - "hocDisplayNames": null, - "id": 5, - "key": "1", - "type": 5, - }, + 64, + Array [], ], Array [ - 6, - Object { - "children": Array [], - "displayName": "Child", - "hocDisplayNames": Array [ - "Memo", - ], - "id": 6, - "key": null, - "type": 8, - }, + 128, + Array [], ], - ], - }, - Object { - "commitData": Array [ - Object { - "changeDescriptions": Array [ - Array [ - 14, - Object { - "context": null, - "didHooksChange": false, - "isFirstMount": true, - "props": null, - "state": null, - }, - ], - Array [ - 16, - Object { - "context": null, - "didHooksChange": false, - "isFirstMount": true, - "props": null, - "state": null, - }, - ], - Array [ - 17, - Object { - "context": null, - "didHooksChange": false, - "isFirstMount": true, - "props": null, - "state": null, - }, - ], - ], - "duration": 11, - "effectDuration": null, - "fiberActualDurations": Array [ - Array [ - 13, - 11, - ], - Array [ - 14, - 11, - ], - Array [ - 16, - 0, - ], - Array [ - 17, - 1, - ], - ], - "fiberSelfDurations": Array [ - Array [ - 13, - 0, - ], - Array [ - 14, - 10, - ], - Array [ - 16, - 0, - ], - Array [ - 17, - 1, - ], - ], - "passiveEffectDuration": null, - "priorityLevel": "Immediate", - "timestamp": 24, - "updaters": Array [ - Object { - "displayName": "render()", - "hocDisplayNames": null, - "id": 13, - "key": null, - "type": 11, - }, - ], - }, - ], - "displayName": "Parent", - "initialTreeBaseDurations": Array [], - "operations": Array [ Array [ - 1, - 13, - 15, - 6, - 80, - 97, - 114, - 101, - 110, - 116, - 5, - 67, - 104, - 105, - 108, - 100, - 1, - 48, - 1, - 13, - 11, - 0, - 3, - 1, - 1, - 4, - 13, - 11000, - 1, - 14, - 5, - 13, - 0, - 1, - 0, - 4, - 14, - 11000, - 1, - 16, - 5, - 14, - 14, - 2, - 3, - 4, - 16, - 0, - 1, - 17, - 8, - 14, - 14, - 2, - 0, - 4, - 17, - 1000, + 256, + Array [], + ], + Array [ + 512, + Array [], + ], + Array [ + 1024, + Array [], + ], + Array [ + 2048, + Array [], + ], + Array [ + 4096, + Array [], + ], + Array [ + 8192, + Array [], + ], + Array [ + 16384, + Array [], + ], + Array [ + 32768, + Array [], + ], + Array [ + 65536, + Array [], + ], + Array [ + 131072, + Array [], + ], + Array [ + 262144, + Array [], + ], + Array [ + 524288, + Array [], + ], + Array [ + 1048576, + Array [], + ], + Array [ + 2097152, + Array [], + ], + Array [ + 4194304, + Array [], + ], + Array [ + 8388608, + Array [], + ], + Array [ + 16777216, + Array [], + ], + Array [ + 33554432, + Array [], + ], + Array [ + 67108864, + Array [], + ], + Array [ + 134217728, + Array [], + ], + Array [ + 268435456, + Array [], + ], + Array [ + 536870912, + Array [], + ], + Array [ + 1073741824, + Array [], ], ], - "rootID": 13, + "nativeEvents": Array [], + "networkMeasures": Array [], + "otherUserTimingMarks": Array [], + "reactVersion": "", + "schedulingEvents": Array [ + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 23, + "type": "schedule-render", + "warning": null, + }, + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 34, + "type": "schedule-render", + "warning": null, + }, + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "schedule-render", + "warning": null, + }, + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 44, + "type": "schedule-render", + "warning": null, + }, + ], + "snapshotHeight": 0, "snapshots": Array [], + "startTime": 13, + "suspenseEvents": Array [], + "thrownErrors": Array [], }, ], "version": 5, @@ -2983,39 +5005,758 @@ Object { 3, 0, 2, - 0, - 4, + 0, + 4, + 4, + 0, + ], + Array [ + 1, + 1, + 0, + ], + Array [ + 1, + 1, + 0, + ], + Array [ + 1, + 1, + 0, + ], + Array [ + 1, + 1, + 0, + ], + Array [ + 1, + 1, + 0, + ], + ], + "rootID": 1, + "snapshots": Array [], + }, + ], + "timelineData": Array [ + Object { + "batchUIDToMeasuresMap": Array [ + Array [ + 1, + Array [ + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 1, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "layout-effects", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "passive-effects", + }, + ], + ], + Array [ + 2, + Array [ + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + ], + ], + Array [ + 3, + Array [ + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + ], + ], + Array [ + 4, + Array [ + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + ], + ], + Array [ + 5, + Array [ + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + ], + ], + Array [ + 6, + Array [ + Object { + "batchUID": 6, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 6, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 6, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + ], + ], + ], + "componentMeasures": Array [ + Object { + "componentName": "Component", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Component", + "duration": 0, + "timestamp": 10, + "type": "layout-effect-mount", + "warning": null, + }, + Object { + "componentName": "Component", + "duration": 0, + "timestamp": 10, + "type": "passive-effect-mount", + "warning": null, + }, + Object { + "componentName": "Component", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Component", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Component", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Component", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Component", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + ], + "duration": 20, + "flamechart": Array [], + "internalModuleSourceToRanges": Array [], + "laneToLabelMap": Array [ + Array [ + 1, + "Sync", + ], + Array [ + 2, + "InputContinuousHydration", + ], + Array [ + 4, + "InputContinuous", + ], + Array [ + 8, + "DefaultHydration", + ], + Array [ + 16, + "Default", + ], + Array [ + 32, + "TransitionHydration", + ], + Array [ + 64, + "Transition", + ], + Array [ + 128, + "Transition", + ], + Array [ + 256, + "Transition", + ], + Array [ + 512, + "Transition", + ], + Array [ + 1024, + "Transition", + ], + Array [ + 2048, + "Transition", + ], + Array [ + 4096, + "Transition", + ], + Array [ + 8192, + "Transition", + ], + Array [ + 16384, + "Transition", + ], + Array [ + 32768, + "Transition", + ], + Array [ + 65536, + "Transition", + ], + Array [ + 131072, + "Transition", + ], + Array [ + 262144, + "Transition", + ], + Array [ + 524288, + "Transition", + ], + Array [ + 1048576, + "Transition", + ], + Array [ + 2097152, + "Transition", + ], + Array [ + 4194304, + "Retry", + ], + Array [ + 8388608, + "Retry", + ], + Array [ + 16777216, + "Retry", + ], + Array [ + 33554432, + "Retry", + ], + Array [ + 67108864, + "Retry", + ], + Array [ + 134217728, + "SelectiveHydration", + ], + Array [ + 268435456, + "IdleHydration", + ], + Array [ + 536870912, + "Idle", + ], + Array [ + 1073741824, + "Offscreen", + ], + ], + "laneToReactMeasureMap": Array [ + Array [ + 1, + Array [ + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 1, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "layout-effects", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "passive-effects", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 6, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 6, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 6, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + ], + ], + Array [ + 2, + Array [], + ], + Array [ 4, - 0, + Array [], ], Array [ - 1, - 1, - 0, + 8, + Array [], ], Array [ - 1, - 1, - 0, + 16, + Array [], ], Array [ - 1, - 1, - 0, + 32, + Array [], ], Array [ - 1, - 1, - 0, + 64, + Array [], ], Array [ - 1, - 1, - 0, + 128, + Array [], + ], + Array [ + 256, + Array [], + ], + Array [ + 512, + Array [], + ], + Array [ + 1024, + Array [], + ], + Array [ + 2048, + Array [], + ], + Array [ + 4096, + Array [], + ], + Array [ + 8192, + Array [], + ], + Array [ + 16384, + Array [], + ], + Array [ + 32768, + Array [], + ], + Array [ + 65536, + Array [], + ], + Array [ + 131072, + Array [], + ], + Array [ + 262144, + Array [], + ], + Array [ + 524288, + Array [], + ], + Array [ + 1048576, + Array [], + ], + Array [ + 2097152, + Array [], + ], + Array [ + 4194304, + Array [], + ], + Array [ + 8388608, + Array [], + ], + Array [ + 16777216, + Array [], + ], + Array [ + 33554432, + Array [], + ], + Array [ + 67108864, + Array [], + ], + Array [ + 134217728, + Array [], + ], + Array [ + 268435456, + Array [], + ], + Array [ + 536870912, + Array [], + ], + Array [ + 1073741824, + Array [], ], ], - "rootID": 1, + "nativeEvents": Array [], + "networkMeasures": Array [], + "otherUserTimingMarks": Array [], + "reactVersion": "", + "schedulingEvents": Array [ + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "componentName": "Component", + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-state-update", + "warning": null, + }, + Object { + "componentName": "Component", + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-state-update", + "warning": null, + }, + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + ], + "snapshotHeight": 0, "snapshots": Array [], + "startTime": -10, + "suspenseEvents": Array [], + "thrownErrors": Array [], }, ], "version": 5, @@ -4253,67 +6994,829 @@ Object { 1, 4, 1, - 3, + 3, + 2, + 3, + 0, + 4, + 4, + 0, + 1, + 5, + 5, + 4, + 4, + 4, + 0, + 4, + 5, + 0, + 1, + 6, + 1, + 3, + 2, + 5, + 0, + 4, + 6, + 0, + 1, + 7, + 5, + 6, + 6, + 4, + 0, + 4, + 7, + 0, + ], + Array [ + 1, + 1, + 0, + ], + Array [ + 1, + 1, + 0, + ], + Array [ + 1, + 1, + 0, + ], + Array [ + 1, + 1, + 0, + ], + ], + "rootID": 1, + "snapshots": Array [], + }, + ], + "timelineData": Array [ + Object { + "batchUIDToMeasuresMap": Array [ + Array [ + 1, + Array [ + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 1, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "layout-effects", + }, + ], + ], + Array [ + 2, + Array [ + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + ], + ], + Array [ + 3, + Array [ + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + ], + ], + Array [ + 4, + Array [ + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + ], + ], + Array [ + 5, + Array [ + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + ], + ], + ], + "componentMeasures": Array [ + Object { + "componentName": "LegacyContextProvider", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "ModernContextConsumer", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "FunctionComponentWithHooks", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "LegacyContextConsumer", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "FunctionComponentWithHooks", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "LegacyContextProvider", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "ModernContextConsumer", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "FunctionComponentWithHooks", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "LegacyContextConsumer", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "FunctionComponentWithHooks", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "LegacyContextProvider", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "ModernContextConsumer", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "FunctionComponentWithHooks", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "LegacyContextConsumer", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "FunctionComponentWithHooks", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "LegacyContextProvider", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "ModernContextConsumer", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "FunctionComponentWithHooks", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "LegacyContextConsumer", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "FunctionComponentWithHooks", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "LegacyContextProvider", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "ModernContextConsumer", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "FunctionComponentWithHooks", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "LegacyContextConsumer", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "FunctionComponentWithHooks", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + ], + "duration": 20, + "flamechart": Array [], + "internalModuleSourceToRanges": Array [], + "laneToLabelMap": Array [ + Array [ + 1, + "Sync", + ], + Array [ 2, - 3, - 0, - 4, - 4, - 0, - 1, - 5, - 5, - 4, - 4, - 4, - 0, + "InputContinuousHydration", + ], + Array [ 4, - 5, - 0, - 1, - 6, + "InputContinuous", + ], + Array [ + 8, + "DefaultHydration", + ], + Array [ + 16, + "Default", + ], + Array [ + 32, + "TransitionHydration", + ], + Array [ + 64, + "Transition", + ], + Array [ + 128, + "Transition", + ], + Array [ + 256, + "Transition", + ], + Array [ + 512, + "Transition", + ], + Array [ + 1024, + "Transition", + ], + Array [ + 2048, + "Transition", + ], + Array [ + 4096, + "Transition", + ], + Array [ + 8192, + "Transition", + ], + Array [ + 16384, + "Transition", + ], + Array [ + 32768, + "Transition", + ], + Array [ + 65536, + "Transition", + ], + Array [ + 131072, + "Transition", + ], + Array [ + 262144, + "Transition", + ], + Array [ + 524288, + "Transition", + ], + Array [ + 1048576, + "Transition", + ], + Array [ + 2097152, + "Transition", + ], + Array [ + 4194304, + "Retry", + ], + Array [ + 8388608, + "Retry", + ], + Array [ + 16777216, + "Retry", + ], + Array [ + 33554432, + "Retry", + ], + Array [ + 67108864, + "Retry", + ], + Array [ + 134217728, + "SelectiveHydration", + ], + Array [ + 268435456, + "IdleHydration", + ], + Array [ + 536870912, + "Idle", + ], + Array [ + 1073741824, + "Offscreen", + ], + ], + "laneToReactMeasureMap": Array [ + Array [ 1, - 3, + Array [ + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 1, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "layout-effects", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 3, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 4, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 5, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + ], + ], + Array [ 2, - 5, - 0, - 4, - 6, - 0, - 1, - 7, - 5, - 6, - 6, - 4, - 0, + Array [], + ], + Array [ 4, - 7, - 0, + Array [], ], Array [ - 1, - 1, - 0, + 8, + Array [], ], Array [ - 1, - 1, - 0, + 16, + Array [], ], Array [ - 1, - 1, - 0, + 32, + Array [], ], Array [ - 1, - 1, - 0, + 64, + Array [], + ], + Array [ + 128, + Array [], + ], + Array [ + 256, + Array [], + ], + Array [ + 512, + Array [], + ], + Array [ + 1024, + Array [], + ], + Array [ + 2048, + Array [], + ], + Array [ + 4096, + Array [], + ], + Array [ + 8192, + Array [], + ], + Array [ + 16384, + Array [], + ], + Array [ + 32768, + Array [], + ], + Array [ + 65536, + Array [], + ], + Array [ + 131072, + Array [], + ], + Array [ + 262144, + Array [], + ], + Array [ + 524288, + Array [], + ], + Array [ + 1048576, + Array [], + ], + Array [ + 2097152, + Array [], + ], + Array [ + 4194304, + Array [], + ], + Array [ + 8388608, + Array [], + ], + Array [ + 16777216, + Array [], + ], + Array [ + 33554432, + Array [], + ], + Array [ + 67108864, + Array [], + ], + Array [ + 134217728, + Array [], + ], + Array [ + 268435456, + Array [], + ], + Array [ + 536870912, + Array [], + ], + Array [ + 1073741824, + Array [], ], ], - "rootID": 1, + "nativeEvents": Array [], + "networkMeasures": Array [], + "otherUserTimingMarks": Array [], + "reactVersion": "", + "schedulingEvents": Array [ + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "componentName": "LegacyContextProvider", + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-state-update", + "warning": null, + }, + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + ], + "snapshotHeight": 0, "snapshots": Array [], + "startTime": -10, + "suspenseEvents": Array [], + "thrownErrors": Array [], }, ], "version": 5, diff --git a/packages/react-devtools-shared/src/__tests__/preprocessData-test.js b/packages/react-devtools-shared/src/__tests__/preprocessData-test.js index a28f6461680b2..a79b943c30d56 100644 --- a/packages/react-devtools-shared/src/__tests__/preprocessData-test.js +++ b/packages/react-devtools-shared/src/__tests__/preprocessData-test.js @@ -30,11 +30,11 @@ describe('Timeline profiler', () => { // Remove file-system specific bits or version-specific bits of information from the module range marks. function filterMarkData(markName) { if (markName.startsWith('--react-internal-module-start')) { - return `${markName.substr(0, 29)}-`; + return '--react-internal-module-start- at filtered (:0:0)'; } else if (markName.startsWith('--react-internal-module-stop')) { - return `${markName.substr(0, 28)}-`; + return '--react-internal-module-stop- at filtered (:1:1)'; } else if (markName.startsWith('--react-version')) { - return `${markName.substr(0, 15)}-0.0.0`; + return '--react-version-'; } else { return markName; } @@ -181,7 +181,7 @@ describe('Timeline profiler', () => { function createReactVersionEntry() { return createUserTimingEntry({ cat: 'blink.user_timing', - name: '--react-version-0.0.0', + name: '--react-version-', }); } @@ -390,7 +390,7 @@ describe('Timeline profiler', () => { "nativeEvents": Array [], "networkMeasures": Array [], "otherUserTimingMarks": Array [], - "reactVersion": "0.0.0", + "reactVersion": "", "schedulingEvents": Array [], "snapshotHeight": 0, "snapshots": Array [], @@ -439,183 +439,165 @@ describe('Timeline profiler', () => { }), ]); expect(data).toMatchInlineSnapshot(` + Object { + "batchUIDToMeasuresMap": Map { + 0 => Array [ Object { - "batchUIDToMeasuresMap": Map { - 0 => Array [ - Object { - "batchUID": 0, - "depth": 0, - "duration": 0.005, - "lanes": Array [ - 9, - ], - "timestamp": 0.006, - "type": "render-idle", - }, - Object { - "batchUID": 0, - "depth": 0, - "duration": 0.001, - "lanes": Array [ - 9, - ], - "timestamp": 0.006, - "type": "render", - }, - Object { - "batchUID": 0, - "depth": 0, - "duration": 0.003, - "lanes": Array [ - 9, - ], - "timestamp": 0.008, - "type": "commit", - }, - Object { - "batchUID": 0, - "depth": 1, - "duration": 0.001, - "lanes": Array [ - 9, - ], - "timestamp": 0.009, - "type": "layout-effects", - }, - ], - }, - "componentMeasures": Array [], - "duration": 0.011, - "flamechart": Array [], - "internalModuleSourceToRanges": Map {}, - "laneToLabelMap": Map { - 0 => "Sync", - 1 => "InputContinuousHydration", - 2 => "InputContinuous", - 3 => "DefaultHydration", - 4 => "Default", - 5 => "TransitionHydration", - 6 => "Transition", - 7 => "Transition", - 8 => "Transition", - 9 => "Transition", - 10 => "Transition", - 11 => "Transition", - 12 => "Transition", - 13 => "Transition", - 14 => "Transition", - 15 => "Transition", - 16 => "Transition", - 17 => "Transition", - 18 => "Transition", - 19 => "Transition", - 20 => "Transition", - 21 => "Transition", - 22 => "Retry", - 23 => "Retry", - 24 => "Retry", - 25 => "Retry", - 26 => "Retry", - 27 => "SelectiveHydration", - 28 => "IdleHydration", - 29 => "Idle", - 30 => "Offscreen", - }, - "laneToReactMeasureMap": Map { - 0 => Array [], - 1 => Array [], - 2 => Array [], - 3 => Array [], - 4 => Array [], - 5 => Array [], - 6 => Array [], - 7 => Array [], - 8 => Array [], - 9 => Array [ - Object { - "batchUID": 0, - "depth": 0, - "duration": 0.005, - "lanes": Array [ - 9, - ], - "timestamp": 0.006, - "type": "render-idle", - }, - Object { - "batchUID": 0, - "depth": 0, - "duration": 0.001, - "lanes": Array [ - 9, - ], - "timestamp": 0.006, - "type": "render", - }, - Object { - "batchUID": 0, - "depth": 0, - "duration": 0.003, - "lanes": Array [ - 9, - ], - "timestamp": 0.008, - "type": "commit", - }, - Object { - "batchUID": 0, - "depth": 1, - "duration": 0.001, - "lanes": Array [ - 9, - ], - "timestamp": 0.009, - "type": "layout-effects", - }, - ], - 10 => Array [], - 11 => Array [], - 12 => Array [], - 13 => Array [], - 14 => Array [], - 15 => Array [], - 16 => Array [], - 17 => Array [], - 18 => Array [], - 19 => Array [], - 20 => Array [], - 21 => Array [], - 22 => Array [], - 23 => Array [], - 24 => Array [], - 25 => Array [], - 26 => Array [], - 27 => Array [], - 28 => Array [], - 29 => Array [], - 30 => Array [], - }, - "nativeEvents": Array [], - "networkMeasures": Array [], - "otherUserTimingMarks": Array [], - "reactVersion": "0.0.0", - "schedulingEvents": Array [ - Object { - "lanes": Array [ - 9, - ], - "timestamp": 0.005, - "type": "schedule-render", - "warning": null, - }, - ], - "snapshotHeight": 0, - "snapshots": Array [], - "startTime": 1, - "suspenseEvents": Array [], - "thrownErrors": Array [], - } - `); + "batchUID": 0, + "depth": 0, + "duration": 0.005, + "lanes": "0b0000000000000000000000000001001", + "timestamp": 0.006, + "type": "render-idle", + }, + Object { + "batchUID": 0, + "depth": 0, + "duration": 0.001, + "lanes": "0b0000000000000000000000000001001", + "timestamp": 0.006, + "type": "render", + }, + Object { + "batchUID": 0, + "depth": 0, + "duration": 0.003, + "lanes": "0b0000000000000000000000000001001", + "timestamp": 0.008, + "type": "commit", + }, + Object { + "batchUID": 0, + "depth": 1, + "duration": 0.001, + "lanes": "0b0000000000000000000000000001001", + "timestamp": 0.009, + "type": "layout-effects", + }, + ], + }, + "componentMeasures": Array [], + "duration": 0.011, + "flamechart": Array [], + "internalModuleSourceToRanges": Map {}, + "laneToLabelMap": Map { + 0 => "Sync", + 1 => "InputContinuousHydration", + 2 => "InputContinuous", + 3 => "DefaultHydration", + 4 => "Default", + 5 => "TransitionHydration", + 6 => "Transition", + 7 => "Transition", + 8 => "Transition", + 9 => "Transition", + 10 => "Transition", + 11 => "Transition", + 12 => "Transition", + 13 => "Transition", + 14 => "Transition", + 15 => "Transition", + 16 => "Transition", + 17 => "Transition", + 18 => "Transition", + 19 => "Transition", + 20 => "Transition", + 21 => "Transition", + 22 => "Retry", + 23 => "Retry", + 24 => "Retry", + 25 => "Retry", + 26 => "Retry", + 27 => "SelectiveHydration", + 28 => "IdleHydration", + 29 => "Idle", + 30 => "Offscreen", + }, + "laneToReactMeasureMap": Map { + 0 => Array [], + 1 => Array [], + 2 => Array [], + 3 => Array [], + 4 => Array [], + 5 => Array [], + 6 => Array [], + 7 => Array [], + 8 => Array [], + 9 => Array [ + Object { + "batchUID": 0, + "depth": 0, + "duration": 0.005, + "lanes": "0b0000000000000000000000000001001", + "timestamp": 0.006, + "type": "render-idle", + }, + Object { + "batchUID": 0, + "depth": 0, + "duration": 0.001, + "lanes": "0b0000000000000000000000000001001", + "timestamp": 0.006, + "type": "render", + }, + Object { + "batchUID": 0, + "depth": 0, + "duration": 0.003, + "lanes": "0b0000000000000000000000000001001", + "timestamp": 0.008, + "type": "commit", + }, + Object { + "batchUID": 0, + "depth": 1, + "duration": 0.001, + "lanes": "0b0000000000000000000000000001001", + "timestamp": 0.009, + "type": "layout-effects", + }, + ], + 10 => Array [], + 11 => Array [], + 12 => Array [], + 13 => Array [], + 14 => Array [], + 15 => Array [], + 16 => Array [], + 17 => Array [], + 18 => Array [], + 19 => Array [], + 20 => Array [], + 21 => Array [], + 22 => Array [], + 23 => Array [], + 24 => Array [], + 25 => Array [], + 26 => Array [], + 27 => Array [], + 28 => Array [], + 29 => Array [], + 30 => Array [], + }, + "nativeEvents": Array [], + "networkMeasures": Array [], + "otherUserTimingMarks": Array [], + "reactVersion": "", + "schedulingEvents": Array [ + Object { + "lanes": "0b0000000000000000000000000001001", + "timestamp": 0.005, + "type": "schedule-render", + "warning": null, + }, + ], + "snapshotHeight": 0, + "snapshots": Array [], + "startTime": 1, + "suspenseEvents": Array [], + "thrownErrors": Array [], + } + `); }); it('should process a sample legacy render sequence', async () => { @@ -633,55 +615,53 @@ describe('Timeline profiler', () => { "batchUID": 0, "depth": 0, "duration": 0.01, - "lanes": Array [ - 0, - ], - "timestamp": 0.004, + "lanes": "0b0000000000000000000000000000000", + "timestamp": 0.006, "type": "render-idle", }, Object { "batchUID": 0, "depth": 0, "duration": 0.001, - "lanes": Array [ - 0, - ], - "timestamp": 0.004, + "lanes": "0b0000000000000000000000000000000", + "timestamp": 0.006, "type": "render", }, Object { "batchUID": 0, "depth": 0, "duration": 0.008, - "lanes": Array [ - 0, - ], - "timestamp": 0.006, + "lanes": "0b0000000000000000000000000000000", + "timestamp": 0.008, "type": "commit", }, Object { "batchUID": 0, "depth": 1, "duration": 0.001, - "lanes": Array [ - 0, - ], - "timestamp": 0.012, + "lanes": "0b0000000000000000000000000000000", + "timestamp": 0.014, "type": "layout-effects", }, ], }, "componentMeasures": Array [], - "duration": 0.014, + "duration": 0.016, "flamechart": Array [], "internalModuleSourceToRanges": Map { undefined => Array [ Array [ Object { - "functionName": "", + "columnNumber": 0, + "functionName": "filtered", + "lineNumber": 0, + "source": " at filtered (:0:0)", }, Object { - "functionName": "dule-stop-", + "columnNumber": 1, + "functionName": "filtered", + "lineNumber": 1, + "source": " at filtered (:1:1)", }, ], ], @@ -725,40 +705,32 @@ describe('Timeline profiler', () => { "batchUID": 0, "depth": 0, "duration": 0.01, - "lanes": Array [ - 0, - ], - "timestamp": 0.004, + "lanes": "0b0000000000000000000000000000000", + "timestamp": 0.006, "type": "render-idle", }, Object { "batchUID": 0, "depth": 0, "duration": 0.001, - "lanes": Array [ - 0, - ], - "timestamp": 0.004, + "lanes": "0b0000000000000000000000000000000", + "timestamp": 0.006, "type": "render", }, Object { "batchUID": 0, "depth": 0, "duration": 0.008, - "lanes": Array [ - 0, - ], - "timestamp": 0.006, + "lanes": "0b0000000000000000000000000000000", + "timestamp": 0.008, "type": "commit", }, Object { "batchUID": 0, "depth": 1, "duration": 0.001, - "lanes": Array [ - 0, - ], - "timestamp": 0.012, + "lanes": "0b0000000000000000000000000000000", + "timestamp": 0.014, "type": "layout-effects", }, ], @@ -796,13 +768,11 @@ describe('Timeline profiler', () => { "nativeEvents": Array [], "networkMeasures": Array [], "otherUserTimingMarks": Array [], - "reactVersion": "0.0.0", + "reactVersion": "", "schedulingEvents": Array [ Object { - "lanes": Array [ - 0, - ], - "timestamp": 0.003, + "lanes": "0b0000000000000000000000000000000", + "timestamp": 0.005, "type": "schedule-render", "warning": null, }, @@ -842,50 +812,40 @@ describe('Timeline profiler', () => { "batchUID": 0, "depth": 0, "duration": 0.012, - "lanes": Array [ - 4, - ], - "timestamp": 0.004, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.006, "type": "render-idle", }, Object { "batchUID": 0, "depth": 0, "duration": 0.003, - "lanes": Array [ - 4, - ], - "timestamp": 0.004, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.006, "type": "render", }, Object { "batchUID": 0, "depth": 0, "duration": 0.008, - "lanes": Array [ - 4, - ], - "timestamp": 0.008, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.01, "type": "commit", }, Object { "batchUID": 0, "depth": 1, "duration": 0.001, - "lanes": Array [ - 4, - ], - "timestamp": 0.014, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.016, "type": "layout-effects", }, Object { "batchUID": 0, "depth": 0, "duration": 0.004, - "lanes": Array [ - 4, - ], - "timestamp": 0.017, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.019, "type": "passive-effects", }, ], @@ -894,50 +854,40 @@ describe('Timeline profiler', () => { "batchUID": 1, "depth": 0, "duration": 0.012, - "lanes": Array [ - 4, - ], - "timestamp": 0.022, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.024, "type": "render-idle", }, Object { "batchUID": 1, "depth": 0, "duration": 0.003, - "lanes": Array [ - 4, - ], - "timestamp": 0.022, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.024, "type": "render", }, Object { "batchUID": 1, "depth": 0, "duration": 0.008, - "lanes": Array [ - 4, - ], - "timestamp": 0.026, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.028, "type": "commit", }, Object { "batchUID": 1, "depth": 1, "duration": 0.001, - "lanes": Array [ - 4, - ], - "timestamp": 0.032, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.034, "type": "layout-effects", }, Object { "batchUID": 1, "depth": 0, "duration": 0.003, - "lanes": Array [ - 4, - ], - "timestamp": 0.035, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.037, "type": "passive-effects", }, ], @@ -946,42 +896,48 @@ describe('Timeline profiler', () => { Object { "componentName": "App", "duration": 0.001, - "timestamp": 0.005, + "timestamp": 0.007, "type": "render", "warning": null, }, Object { "componentName": "App", "duration": 0.002, - "timestamp": 0.018, + "timestamp": 0.02, "type": "passive-effect-mount", "warning": null, }, Object { "componentName": "App", "duration": 0.001, - "timestamp": 0.023, + "timestamp": 0.025, "type": "render", "warning": null, }, Object { "componentName": "App", "duration": 0.001, - "timestamp": 0.036, + "timestamp": 0.038, "type": "passive-effect-mount", "warning": null, }, ], - "duration": 0.038, + "duration": 0.04, "flamechart": Array [], "internalModuleSourceToRanges": Map { undefined => Array [ Array [ Object { - "functionName": "", + "columnNumber": 0, + "functionName": "filtered", + "lineNumber": 0, + "source": " at filtered (:0:0)", }, Object { - "functionName": "dule-stop-", + "columnNumber": 1, + "functionName": "filtered", + "lineNumber": 1, + "source": " at filtered (:1:1)", }, ], ], @@ -1029,100 +985,80 @@ describe('Timeline profiler', () => { "batchUID": 0, "depth": 0, "duration": 0.012, - "lanes": Array [ - 4, - ], - "timestamp": 0.004, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.006, "type": "render-idle", }, Object { "batchUID": 0, "depth": 0, "duration": 0.003, - "lanes": Array [ - 4, - ], - "timestamp": 0.004, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.006, "type": "render", }, Object { "batchUID": 0, "depth": 0, "duration": 0.008, - "lanes": Array [ - 4, - ], - "timestamp": 0.008, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.01, "type": "commit", }, Object { "batchUID": 0, "depth": 1, "duration": 0.001, - "lanes": Array [ - 4, - ], - "timestamp": 0.014, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.016, "type": "layout-effects", }, Object { "batchUID": 0, "depth": 0, "duration": 0.004, - "lanes": Array [ - 4, - ], - "timestamp": 0.017, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.019, "type": "passive-effects", }, Object { "batchUID": 1, "depth": 0, "duration": 0.012, - "lanes": Array [ - 4, - ], - "timestamp": 0.022, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.024, "type": "render-idle", }, Object { "batchUID": 1, "depth": 0, "duration": 0.003, - "lanes": Array [ - 4, - ], - "timestamp": 0.022, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.024, "type": "render", }, Object { "batchUID": 1, "depth": 0, "duration": 0.008, - "lanes": Array [ - 4, - ], - "timestamp": 0.026, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.028, "type": "commit", }, Object { "batchUID": 1, "depth": 1, "duration": 0.001, - "lanes": Array [ - 4, - ], - "timestamp": 0.032, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.034, "type": "layout-effects", }, Object { "batchUID": 1, "depth": 0, "duration": 0.003, - "lanes": Array [ - 4, - ], - "timestamp": 0.035, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.037, "type": "passive-effects", }, ], @@ -1156,22 +1092,18 @@ describe('Timeline profiler', () => { "nativeEvents": Array [], "networkMeasures": Array [], "otherUserTimingMarks": Array [], - "reactVersion": "0.0.0", + "reactVersion": "", "schedulingEvents": Array [ Object { - "lanes": Array [ - 4, - ], - "timestamp": 0.003, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.005, "type": "schedule-render", "warning": null, }, Object { "componentName": "App", - "lanes": Array [ - 4, - ], - "timestamp": 0.019, + "lanes": "0b0000000000000000000000000000100", + "timestamp": 0.021, "type": "schedule-state-update", "warning": null, }, @@ -1938,7 +1870,11 @@ describe('Timeline profiler', () => { }); }); + // Note the in-memory tests vary slightly (e.g. timestamp values, lane numbers) from the above tests. + // That's okay; the important thing is the the lane-to-label matches the subsequent events/measures. describe('DevTools hook (in memory)', () => { + let store; + beforeEach(() => { utils = require('./utils'); utils.beforeEachProfiling(); @@ -1947,7 +1883,7 @@ describe('Timeline profiler', () => { ReactDOM = require('react-dom'); Scheduler = require('scheduler'); - const store = global.store; + store = global.store; // Start profiling so that data will actually be recorded. utils.act(() => store.profilerStore.startProfiling()); diff --git a/packages/react-devtools-shared/src/__tests__/utils.js b/packages/react-devtools-shared/src/__tests__/utils.js index 03228ea594a0a..a1b4ecca6e642 100644 --- a/packages/react-devtools-shared/src/__tests__/utils.js +++ b/packages/react-devtools-shared/src/__tests__/utils.js @@ -227,6 +227,9 @@ export function exportImportHelper(bridge: FrontendBridge, store: Store): void { expect(profilingDataFrontendInitial.dataForRoots).toEqual( profilingDataFrontend.dataForRoots, ); + expect(profilingDataFrontendInitial.timelineData).toEqual( + profilingDataFrontend.timelineData, + ); // Snapshot the JSON-parsed object, rather than the raw string, because Jest formats the diff nicer. expect(parsedProfilingDataExport).toMatchSnapshot('imported data'); diff --git a/scripts/jest/config.build-devtools.js b/scripts/jest/config.build-devtools.js index a4a2b3fbb7f89..7f0688c272ebc 100644 --- a/scripts/jest/config.build-devtools.js +++ b/scripts/jest/config.build-devtools.js @@ -76,6 +76,9 @@ module.exports = Object.assign({}, baseConfig, { require.resolve( '../../packages/react-devtools-shared/src/__tests__/__serializers__/storeSerializer.js' ), + require.resolve( + '../../packages/react-devtools-shared/src/__tests__/__serializers__/timelineDataSerializer.js' + ), require.resolve( '../../packages/react-devtools-shared/src/__tests__/__serializers__/treeContextStateSerializer.js' ), From 0c180ab048adb40251b5dd27a9efd8baf009e576 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 27 Jan 2022 10:24:39 -0500 Subject: [PATCH 5/9] Wrote new tests for in-memory Timeline profiling --- .../src/__tests__/TimelineProfiler-test.js | 1170 ++++++++++++++++- .../src/__tests__/preprocessData-test.js | 483 ++++++- 2 files changed, 1651 insertions(+), 2 deletions(-) diff --git a/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js b/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js index 914834f3b2d02..5f2ad905dd23a 100644 --- a/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js +++ b/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js @@ -1182,6 +1182,1174 @@ describe('Timeline profiler', () => { unmountFns.forEach(unmountFn => unmountFn()); }); - // TODO (timeline) Write tests + describe('when profiling', () => { + beforeEach(() => { + utils.act(() => store.profilerStore.startProfiling()); + }); + + it('should mark sync render without suspends or state updates', () => { + renderHelper(
); + + const timelineData = stopProfilingAndGetTimelineData(); + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(` + Array [ + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + ] + `); + }); + + it('should mark concurrent render without suspends or state updates', () => { + utils.act(() => renderRootHelper(
)); + + const timelineData = stopProfilingAndGetTimelineData(); + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(` + Array [ + Object { + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + ] + `); + }); + + it('should mark render yields', async () => { + function Bar() { + Scheduler.unstable_yieldValue('Bar'); + return null; + } + + function Foo() { + Scheduler.unstable_yieldValue('Foo'); + return ; + } + + React.startTransition(() => { + renderRootHelper(); + }); + + // Do one step of work. + expect(Scheduler).toFlushAndYieldThrough(['Foo']); + + // Finish flushing so React commits; + // Unless we do this, the ProfilerStore won't collect Profiling data. + expect(Scheduler).toFlushAndYield(['Bar']); + + // Since we yielded, the batch should report two separate "render" chunks. + const batch = getBatchOfWork(0); + expect(batch.filter(({type}) => type === 'render')).toHaveLength(2); + }); + + it('should mark sync render with suspense that resolves', async () => { + let resolveFn; + let resolved = false; + const suspensePromise = new Promise(resolve => { + resolveFn = () => { + resolved = true; + resolve(); + }; + }); + + function Example() { + Scheduler.unstable_yieldValue(resolved ? 'resolved' : 'suspended'); + if (!resolved) { + throw suspensePromise; + } + return null; + } + + renderHelper( + + + , + ); + + expect(Scheduler).toHaveYielded(['suspended']); + + Scheduler.unstable_advanceTime(10); + resolveFn(); + await suspensePromise; + + expect(Scheduler).toFlushAndYield(['resolved']); + + const timelineData = stopProfilingAndGetTimelineData(); + + // Verify the Suspense event and duration was recorded. + expect(timelineData.suspenseEvents).toHaveLength(1); + const suspenseEvent = timelineData.suspenseEvents[0]; + expect(suspenseEvent).toMatchInlineSnapshot(` + Object { + "componentName": "Example", + "depth": 0, + "duration": 10, + "id": "0", + "phase": "mount", + "promiseName": "", + "resolution": "resolved", + "timestamp": 10, + "type": "suspense", + "warning": null, + } + `); + + // There should be two batches of renders: Suspeneded and resolved. + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2); + expect(timelineData.componentMeasures).toHaveLength(2); + }); + + it('should mark sync render with suspense that rejects', async () => { + let rejectFn; + let rejected = false; + const suspensePromise = new Promise((resolve, reject) => { + rejectFn = () => { + rejected = true; + reject(new Error('error')); + }; + }); + + function Example() { + Scheduler.unstable_yieldValue(rejected ? 'rejected' : 'suspended'); + if (!rejected) { + throw suspensePromise; + } + return null; + } + + renderHelper( + + + , + ); + + expect(Scheduler).toHaveYielded(['suspended']); + + Scheduler.unstable_advanceTime(10); + rejectFn(); + await expect(suspensePromise).rejects.toThrow(); + + expect(Scheduler).toHaveYielded(['rejected']); + + const timelineData = stopProfilingAndGetTimelineData(); + + // Verify the Suspense event and duration was recorded. + expect(timelineData.suspenseEvents).toHaveLength(1); + const suspenseEvent = timelineData.suspenseEvents[0]; + expect(suspenseEvent).toMatchInlineSnapshot(` + Object { + "componentName": "Example", + "depth": 0, + "duration": 10, + "id": "0", + "phase": "mount", + "promiseName": "", + "resolution": "rejected", + "timestamp": 10, + "type": "suspense", + "warning": null, + } + `); + + // There should be two batches of renders: Suspeneded and resolved. + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2); + expect(timelineData.componentMeasures).toHaveLength(2); + }); + + it('should mark concurrent render with suspense that resolves', async () => { + let resolveFn; + let resolved = false; + const suspensePromise = new Promise(resolve => { + resolveFn = () => { + resolved = true; + resolve(); + }; + }); + + function Example() { + Scheduler.unstable_yieldValue(resolved ? 'resolved' : 'suspended'); + if (!resolved) { + throw suspensePromise; + } + return null; + } + + renderRootHelper( + + + , + ); + + expect(Scheduler).toFlushAndYield(['suspended']); + + Scheduler.unstable_advanceTime(10); + resolveFn(); + await suspensePromise; + + expect(Scheduler).toFlushAndYield(['resolved']); + + const timelineData = stopProfilingAndGetTimelineData(); + + // Verify the Suspense event and duration was recorded. + expect(timelineData.suspenseEvents).toHaveLength(1); + const suspenseEvent = timelineData.suspenseEvents[0]; + expect(suspenseEvent).toMatchInlineSnapshot(` + Object { + "componentName": "Example", + "depth": 0, + "duration": 10, + "id": "0", + "phase": "mount", + "promiseName": "", + "resolution": "resolved", + "timestamp": 10, + "type": "suspense", + "warning": null, + } + `); + + // There should be two batches of renders: Suspeneded and resolved. + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2); + expect(timelineData.componentMeasures).toHaveLength(2); + }); + + it('should mark concurrent render with suspense that rejects', async () => { + let rejectFn; + let rejected = false; + const suspensePromise = new Promise((resolve, reject) => { + rejectFn = () => { + rejected = true; + reject(new Error('error')); + }; + }); + + function Example() { + Scheduler.unstable_yieldValue(rejected ? 'rejected' : 'suspended'); + if (!rejected) { + throw suspensePromise; + } + return null; + } + + renderRootHelper( + + + , + ); + + expect(Scheduler).toFlushAndYield(['suspended']); + + Scheduler.unstable_advanceTime(10); + rejectFn(); + await expect(suspensePromise).rejects.toThrow(); + + expect(Scheduler).toFlushAndYield(['rejected']); + + const timelineData = stopProfilingAndGetTimelineData(); + + // Verify the Suspense event and duration was recorded. + expect(timelineData.suspenseEvents).toHaveLength(1); + const suspenseEvent = timelineData.suspenseEvents[0]; + expect(suspenseEvent).toMatchInlineSnapshot(` + Object { + "componentName": "Example", + "depth": 0, + "duration": 10, + "id": "0", + "phase": "mount", + "promiseName": "", + "resolution": "rejected", + "timestamp": 10, + "type": "suspense", + "warning": null, + } + `); + + // There should be two batches of renders: Suspeneded and resolved. + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2); + expect(timelineData.componentMeasures).toHaveLength(2); + }); + + it('should mark cascading class component state updates', () => { + class Example extends React.Component { + state = {didMount: false}; + componentDidMount() { + this.setState({didMount: true}); + } + render() { + Scheduler.unstable_advanceTime(10); + Scheduler.unstable_yieldValue( + this.state.didMount ? 'update' : 'mount', + ); + return null; + } + } + + renderRootHelper(); + + expect(Scheduler).toFlushUntilNextPaint(['mount', 'update']); + + const timelineData = stopProfilingAndGetTimelineData(); + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2); + expect(timelineData.componentMeasures).toMatchInlineSnapshot(` + Array [ + Object { + "componentName": "Example", + "duration": 10, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Example", + "duration": 10, + "timestamp": 20, + "type": "render", + "warning": null, + }, + ] + `); + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(` + Array [ + Object { + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "componentName": "Example", + "lanes": "0b0000000000000000000000000000001", + "timestamp": 20, + "type": "schedule-state-update", + "warning": null, + }, + ] + `); + }); + + it('should mark cascading class component force updates', () => { + let forced = false; + class Example extends React.Component { + componentDidMount() { + forced = true; + this.forceUpdate(); + } + render() { + Scheduler.unstable_advanceTime(10); + Scheduler.unstable_yieldValue(forced ? 'force update' : 'mount'); + return null; + } + } + + renderRootHelper(); + + expect(Scheduler).toFlushUntilNextPaint(['mount', 'force update']); + + const timelineData = stopProfilingAndGetTimelineData(); + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2); + expect(timelineData.componentMeasures).toMatchInlineSnapshot(` + Array [ + Object { + "componentName": "Example", + "duration": 10, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Example", + "duration": 10, + "timestamp": 20, + "type": "render", + "warning": null, + }, + ] + `); + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(` + Array [ + Object { + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "componentName": "Example", + "lanes": "0b0000000000000000000000000000001", + "timestamp": 20, + "type": "schedule-force-update", + "warning": null, + }, + ] + `); + }); + + it('should mark render phase state updates for class component', () => { + class Example extends React.Component { + state = {didRender: false}; + render() { + if (this.state.didRender === false) { + this.setState({didRender: true}); + } + Scheduler.unstable_advanceTime(10); + Scheduler.unstable_yieldValue( + this.state.didRender ? 'second render' : 'first render', + ); + return null; + } + } + + renderRootHelper(); + + let errorMessage; + spyOn(console, 'error').and.callFake(message => { + errorMessage = message; + }); + + expect(Scheduler).toFlushAndYield(['first render', 'second render']); + + expect(console.error).toHaveBeenCalledTimes(1); + expect(errorMessage).toContain( + 'Cannot update during an existing state transition', + ); + + const timelineData = stopProfilingAndGetTimelineData(); + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2); + expect(timelineData.componentMeasures).toMatchInlineSnapshot(` + Array [ + Object { + "componentName": "Example", + "duration": 10, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Example", + "duration": 10, + "timestamp": 20, + "type": "render", + "warning": null, + }, + ] + `); + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(` + Array [ + Object { + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "componentName": "Example", + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "schedule-state-update", + "warning": null, + }, + ] + `); + }); + + it('should mark render phase force updates for class component', () => { + let forced = false; + class Example extends React.Component { + render() { + Scheduler.unstable_advanceTime(10); + Scheduler.unstable_yieldValue(forced ? 'force update' : 'render'); + if (!forced) { + forced = true; + this.forceUpdate(); + } + return null; + } + } + + renderRootHelper(); + + let errorMessage; + spyOn(console, 'error').and.callFake(message => { + errorMessage = message; + }); + + expect(Scheduler).toFlushAndYield(['render', 'force update']); + + expect(console.error).toHaveBeenCalledTimes(1); + expect(errorMessage).toContain( + 'Cannot update during an existing state transition', + ); + + const timelineData = stopProfilingAndGetTimelineData(); + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2); + expect(timelineData.componentMeasures).toMatchInlineSnapshot(` + Array [ + Object { + "componentName": "Example", + "duration": 10, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Example", + "duration": 10, + "timestamp": 20, + "type": "render", + "warning": null, + }, + ] + `); + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(` + Array [ + Object { + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "componentName": "Example", + "lanes": "0b0000000000000000000000000010000", + "timestamp": 20, + "type": "schedule-force-update", + "warning": null, + }, + ] + `); + }); + + it('should mark cascading layout updates', () => { + function Example() { + const [didMount, setDidMount] = React.useState(false); + React.useLayoutEffect(() => { + Scheduler.unstable_advanceTime(1); + setDidMount(true); + }, []); + Scheduler.unstable_advanceTime(10); + Scheduler.unstable_yieldValue(didMount ? 'update' : 'mount'); + return didMount; + } + + renderRootHelper(); + + expect(Scheduler).toFlushAndYield(['mount', 'update']); + + const timelineData = stopProfilingAndGetTimelineData(); + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2); + expect(timelineData.componentMeasures).toMatchInlineSnapshot(` + Array [ + Object { + "componentName": "Example", + "duration": 10, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Example", + "duration": 1, + "timestamp": 20, + "type": "layout-effect-mount", + "warning": null, + }, + Object { + "componentName": "Example", + "duration": 10, + "timestamp": 21, + "type": "render", + "warning": null, + }, + ] + `); + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(` + Array [ + Object { + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "componentName": "Example", + "lanes": "0b0000000000000000000000000000001", + "timestamp": 21, + "type": "schedule-state-update", + "warning": null, + }, + ] + `); + }); + + it('should mark cascading passive updates', () => { + function Example() { + const [didMount, setDidMount] = React.useState(false); + React.useEffect(() => { + Scheduler.unstable_advanceTime(1); + setDidMount(true); + }, []); + Scheduler.unstable_advanceTime(10); + Scheduler.unstable_yieldValue(didMount ? 'update' : 'mount'); + return didMount; + } + + renderRootHelper(); + expect(Scheduler).toFlushAndYield(['mount', 'update']); + + const timelineData = stopProfilingAndGetTimelineData(); + expect(timelineData.batchUIDToMeasuresMap.size).toBe(2); + expect(timelineData.componentMeasures).toMatchInlineSnapshot(` + Array [ + Object { + "componentName": "Example", + "duration": 10, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "Example", + "duration": 1, + "timestamp": 20, + "type": "passive-effect-mount", + "warning": null, + }, + Object { + "componentName": "Example", + "duration": 10, + "timestamp": 21, + "type": "render", + "warning": null, + }, + ] + `); + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(` + Array [ + Object { + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "componentName": "Example", + "lanes": "0b0000000000000000000000000010000", + "timestamp": 21, + "type": "schedule-state-update", + "warning": null, + }, + ] + `); + }); + + it('should mark render phase updates', () => { + function Example() { + const [didRender, setDidRender] = React.useState(false); + Scheduler.unstable_advanceTime(10); + if (!didRender) { + setDidRender(true); + } + Scheduler.unstable_yieldValue(didRender ? 'update' : 'mount'); + return didRender; + } + + renderRootHelper(); + expect(Scheduler).toFlushAndYield(['mount', 'update']); + + const timelineData = stopProfilingAndGetTimelineData(); + // Render phase updates should be retried as part of the same batch. + expect(timelineData.batchUIDToMeasuresMap.size).toBe(1); + expect(timelineData.componentMeasures).toMatchInlineSnapshot(` + Array [ + Object { + "componentName": "Example", + "duration": 20, + "timestamp": 10, + "type": "render", + "warning": null, + }, + ] + `); + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(` + Array [ + Object { + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "componentName": "Example", + "lanes": "0b0000000000000000000000000010000", + "timestamp": 20, + "type": "schedule-state-update", + "warning": null, + }, + ] + `); + }); + + it('should mark sync render that throws', async () => { + spyOn(console, 'error'); + + class ErrorBoundary extends React.Component { + state = {error: null}; + componentDidCatch(error) { + this.setState({error}); + } + render() { + Scheduler.unstable_advanceTime(10); + if (this.state.error) { + Scheduler.unstable_yieldValue('ErrorBoundary fallback'); + return null; + } + Scheduler.unstable_yieldValue('ErrorBoundary render'); + return this.props.children; + } + } + + function ExampleThatThrows() { + Scheduler.unstable_yieldValue('ExampleThatThrows'); + throw Error('Expected error'); + } + + renderHelper( + + + , + ); + + expect(Scheduler).toHaveYielded([ + 'ErrorBoundary render', + 'ExampleThatThrows', + 'ExampleThatThrows', + 'ErrorBoundary fallback', + ]); + + const timelineData = stopProfilingAndGetTimelineData(); + expect(timelineData.componentMeasures).toMatchInlineSnapshot(` + Array [ + Object { + "componentName": "ErrorBoundary", + "duration": 10, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "ExampleThatThrows", + "duration": 0, + "timestamp": 20, + "type": "render", + "warning": null, + }, + Object { + "componentName": "ErrorBoundary", + "duration": 10, + "timestamp": 20, + "type": "render", + "warning": null, + }, + ] + `); + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(` + Array [ + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "componentName": "ErrorBoundary", + "lanes": "0b0000000000000000000000000000001", + "timestamp": 20, + "type": "schedule-state-update", + "warning": null, + }, + ] + `); + expect(timelineData.thrownErrors).toMatchInlineSnapshot(` + Array [ + Object { + "componentName": "ExampleThatThrows", + "message": "Expected error", + "phase": "mount", + "timestamp": 20, + "type": "thrown-error", + }, + ] + `); + }); + + it('should mark concurrent render that throws', async () => { + spyOn(console, 'error'); + + class ErrorBoundary extends React.Component { + state = {error: null}; + componentDidCatch(error) { + this.setState({error}); + } + render() { + Scheduler.unstable_advanceTime(10); + if (this.state.error) { + Scheduler.unstable_yieldValue('ErrorBoundary fallback'); + return null; + } + Scheduler.unstable_yieldValue('ErrorBoundary render'); + return this.props.children; + } + } + + function ExampleThatThrows() { + Scheduler.unstable_yieldValue('ExampleThatThrows'); + // eslint-disable-next-line no-throw-literal + throw 'Expected error'; + } + + renderRootHelper( + + + , + ); + + expect(Scheduler).toFlushAndYield([ + 'ErrorBoundary render', + 'ExampleThatThrows', + 'ExampleThatThrows', + 'ErrorBoundary render', + 'ExampleThatThrows', + 'ExampleThatThrows', + 'ErrorBoundary fallback', + ]); + + const timelineData = stopProfilingAndGetTimelineData(); + expect(timelineData.componentMeasures).toMatchInlineSnapshot(` + Array [ + Object { + "componentName": "ErrorBoundary", + "duration": 10, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "ExampleThatThrows", + "duration": 0, + "timestamp": 20, + "type": "render", + "warning": null, + }, + Object { + "componentName": "ErrorBoundary", + "duration": 10, + "timestamp": 20, + "type": "render", + "warning": null, + }, + Object { + "componentName": "ExampleThatThrows", + "duration": 0, + "timestamp": 30, + "type": "render", + "warning": null, + }, + Object { + "componentName": "ErrorBoundary", + "duration": 10, + "timestamp": 30, + "type": "render", + "warning": null, + }, + ] + `); + expect(timelineData.schedulingEvents).toMatchInlineSnapshot(` + Array [ + Object { + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "componentName": "ErrorBoundary", + "lanes": "0b0000000000000000000000000000001", + "timestamp": 30, + "type": "schedule-state-update", + "warning": null, + }, + ] + `); + expect(timelineData.thrownErrors).toMatchInlineSnapshot(` + Array [ + Object { + "componentName": "ExampleThatThrows", + "message": "Expected error", + "phase": "mount", + "timestamp": 20, + "type": "thrown-error", + }, + Object { + "componentName": "ExampleThatThrows", + "message": "Expected error", + "phase": "mount", + "timestamp": 30, + "type": "thrown-error", + }, + ] + `); + }); + + it('should mark passive and layout effects', async () => { + function ComponentWithEffects() { + React.useLayoutEffect(() => { + Scheduler.unstable_yieldValue('layout 1 mount'); + return () => { + Scheduler.unstable_yieldValue('layout 1 unmount'); + }; + }, []); + + React.useEffect(() => { + Scheduler.unstable_yieldValue('passive 1 mount'); + return () => { + Scheduler.unstable_yieldValue('passive 1 unmount'); + }; + }, []); + + React.useLayoutEffect(() => { + Scheduler.unstable_yieldValue('layout 2 mount'); + return () => { + Scheduler.unstable_yieldValue('layout 2 unmount'); + }; + }, []); + + React.useEffect(() => { + Scheduler.unstable_yieldValue('passive 2 mount'); + return () => { + Scheduler.unstable_yieldValue('passive 2 unmount'); + }; + }, []); + + React.useEffect(() => { + Scheduler.unstable_yieldValue('passive 3 mount'); + return () => { + Scheduler.unstable_yieldValue('passive 3 unmount'); + }; + }, []); + + return null; + } + + const unmount = renderRootHelper(); + + expect(Scheduler).toFlushUntilNextPaint([ + 'layout 1 mount', + 'layout 2 mount', + ]); + + expect(Scheduler).toFlushAndYield([ + 'passive 1 mount', + 'passive 2 mount', + 'passive 3 mount', + ]); + + expect(Scheduler).toFlushAndYield([]); + + unmount(); + + expect(Scheduler).toHaveYielded([ + 'layout 1 unmount', + 'layout 2 unmount', + 'passive 1 unmount', + 'passive 2 unmount', + 'passive 3 unmount', + ]); + + const timelineData = stopProfilingAndGetTimelineData(); + expect(timelineData.componentMeasures).toMatchInlineSnapshot(` + Array [ + Object { + "componentName": "ComponentWithEffects", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "ComponentWithEffects", + "duration": 0, + "timestamp": 10, + "type": "layout-effect-mount", + "warning": null, + }, + Object { + "componentName": "ComponentWithEffects", + "duration": 0, + "timestamp": 10, + "type": "layout-effect-mount", + "warning": null, + }, + Object { + "componentName": "ComponentWithEffects", + "duration": 0, + "timestamp": 10, + "type": "passive-effect-mount", + "warning": null, + }, + Object { + "componentName": "ComponentWithEffects", + "duration": 0, + "timestamp": 10, + "type": "passive-effect-mount", + "warning": null, + }, + Object { + "componentName": "ComponentWithEffects", + "duration": 0, + "timestamp": 10, + "type": "passive-effect-mount", + "warning": null, + }, + Object { + "componentName": "ComponentWithEffects", + "duration": 0, + "timestamp": 10, + "type": "layout-effect-unmount", + "warning": null, + }, + Object { + "componentName": "ComponentWithEffects", + "duration": 0, + "timestamp": 10, + "type": "layout-effect-unmount", + "warning": null, + }, + Object { + "componentName": "ComponentWithEffects", + "duration": 0, + "timestamp": 10, + "type": "passive-effect-unmount", + "warning": null, + }, + Object { + "componentName": "ComponentWithEffects", + "duration": 0, + "timestamp": 10, + "type": "passive-effect-unmount", + "warning": null, + }, + Object { + "componentName": "ComponentWithEffects", + "duration": 0, + "timestamp": 10, + "type": "passive-effect-unmount", + "warning": null, + }, + ] + `); + expect(timelineData.batchUIDToMeasuresMap).toMatchInlineSnapshot(` + Map { + 1 => Array [ + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 1, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "layout-effects", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "passive-effects", + }, + ], + 2 => Array [ + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 2, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "layout-effects", + }, + Object { + "batchUID": 2, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "passive-effects", + }, + ], + } + `); + }); + }); + + describe('when not profiling', () => { + it('should not log any marks', () => { + renderHelper(
); + + const timelineData = stopProfilingAndGetTimelineData(); + expect(timelineData).toBeNull(); + }); + }); }); }); diff --git a/packages/react-devtools-shared/src/__tests__/preprocessData-test.js b/packages/react-devtools-shared/src/__tests__/preprocessData-test.js index a79b943c30d56..0d5ebff509b90 100644 --- a/packages/react-devtools-shared/src/__tests__/preprocessData-test.js +++ b/packages/react-devtools-shared/src/__tests__/preprocessData-test.js @@ -1891,6 +1891,487 @@ describe('Timeline profiler', () => { global.IS_REACT_ACT_ENVIRONMENT = true; }); - // TODO (timeline) Write tests + it('should process a sample legacy render sequence', async () => { + utils.legacyRender(
, document.createElement('div')); + utils.act(() => store.profilerStore.stopProfiling()); + + const data = store.profilerStore.profilingData?.timelineData; + expect(data).toHaveLength(1); + const timelineData = data[0]; + expect(timelineData).toMatchInlineSnapshot(` + Object { + "batchUIDToMeasuresMap": Map { + 1 => Array [ + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 1, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "layout-effects", + }, + ], + }, + "componentMeasures": Array [], + "duration": 20, + "flamechart": Array [], + "internalModuleSourceToRanges": Map {}, + "laneToLabelMap": Map { + 1 => "Sync", + 2 => "InputContinuousHydration", + 4 => "InputContinuous", + 8 => "DefaultHydration", + 16 => "Default", + 32 => "TransitionHydration", + 64 => "Transition", + 128 => "Transition", + 256 => "Transition", + 512 => "Transition", + 1024 => "Transition", + 2048 => "Transition", + 4096 => "Transition", + 8192 => "Transition", + 16384 => "Transition", + 32768 => "Transition", + 65536 => "Transition", + 131072 => "Transition", + 262144 => "Transition", + 524288 => "Transition", + 1048576 => "Transition", + 2097152 => "Transition", + 4194304 => "Retry", + 8388608 => "Retry", + 16777216 => "Retry", + 33554432 => "Retry", + 67108864 => "Retry", + 134217728 => "SelectiveHydration", + 268435456 => "IdleHydration", + 536870912 => "Idle", + 1073741824 => "Offscreen", + }, + "laneToReactMeasureMap": Map { + 1 => Array [ + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 1, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "layout-effects", + }, + ], + 2 => Array [], + 4 => Array [], + 8 => Array [], + 16 => Array [], + 32 => Array [], + 64 => Array [], + 128 => Array [], + 256 => Array [], + 512 => Array [], + 1024 => Array [], + 2048 => Array [], + 4096 => Array [], + 8192 => Array [], + 16384 => Array [], + 32768 => Array [], + 65536 => Array [], + 131072 => Array [], + 262144 => Array [], + 524288 => Array [], + 1048576 => Array [], + 2097152 => Array [], + 4194304 => Array [], + 8388608 => Array [], + 16777216 => Array [], + 33554432 => Array [], + 67108864 => Array [], + 134217728 => Array [], + 268435456 => Array [], + 536870912 => Array [], + 1073741824 => Array [], + }, + "nativeEvents": Array [], + "networkMeasures": Array [], + "otherUserTimingMarks": Array [], + "reactVersion": "", + "schedulingEvents": Array [ + Object { + "lanes": "0b0000000000000000000000000000001", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + ], + "snapshotHeight": 0, + "snapshots": Array [], + "startTime": -10, + "suspenseEvents": Array [], + "thrownErrors": Array [], + } + `); + }); + + it('should process a sample createRoot render sequence', async () => { + function App() { + const [didMount, setDidMount] = React.useState(false); + React.useEffect(() => { + if (!didMount) { + setDidMount(true); + } + }); + return true; + } + + const root = ReactDOM.createRoot(document.createElement('div')); + utils.act(() => root.render()); + utils.act(() => store.profilerStore.stopProfiling()); + + const data = store.profilerStore.profilingData?.timelineData; + expect(data).toHaveLength(1); + const timelineData = data[0]; + expect(timelineData).toMatchInlineSnapshot(` + Object { + "batchUIDToMeasuresMap": Map { + 1 => Array [ + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 1, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "layout-effects", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "passive-effects", + }, + ], + 2 => Array [ + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 2, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "layout-effects", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "passive-effects", + }, + ], + }, + "componentMeasures": Array [ + Object { + "componentName": "App", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "App", + "duration": 0, + "timestamp": 10, + "type": "passive-effect-mount", + "warning": null, + }, + Object { + "componentName": "App", + "duration": 0, + "timestamp": 10, + "type": "render", + "warning": null, + }, + Object { + "componentName": "App", + "duration": 0, + "timestamp": 10, + "type": "passive-effect-mount", + "warning": null, + }, + ], + "duration": 20, + "flamechart": Array [], + "internalModuleSourceToRanges": Map {}, + "laneToLabelMap": Map { + 1 => "Sync", + 2 => "InputContinuousHydration", + 4 => "InputContinuous", + 8 => "DefaultHydration", + 16 => "Default", + 32 => "TransitionHydration", + 64 => "Transition", + 128 => "Transition", + 256 => "Transition", + 512 => "Transition", + 1024 => "Transition", + 2048 => "Transition", + 4096 => "Transition", + 8192 => "Transition", + 16384 => "Transition", + 32768 => "Transition", + 65536 => "Transition", + 131072 => "Transition", + 262144 => "Transition", + 524288 => "Transition", + 1048576 => "Transition", + 2097152 => "Transition", + 4194304 => "Retry", + 8388608 => "Retry", + 16777216 => "Retry", + 33554432 => "Retry", + 67108864 => "Retry", + 134217728 => "SelectiveHydration", + 268435456 => "IdleHydration", + 536870912 => "Idle", + 1073741824 => "Offscreen", + }, + "laneToReactMeasureMap": Map { + 1 => Array [], + 2 => Array [], + 4 => Array [], + 8 => Array [], + 16 => Array [ + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 1, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "layout-effects", + }, + Object { + "batchUID": 1, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "passive-effects", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "render-idle", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "render", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "commit", + }, + Object { + "batchUID": 2, + "depth": 1, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "layout-effects", + }, + Object { + "batchUID": 2, + "depth": 0, + "duration": 0, + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "passive-effects", + }, + ], + 32 => Array [], + 64 => Array [], + 128 => Array [], + 256 => Array [], + 512 => Array [], + 1024 => Array [], + 2048 => Array [], + 4096 => Array [], + 8192 => Array [], + 16384 => Array [], + 32768 => Array [], + 65536 => Array [], + 131072 => Array [], + 262144 => Array [], + 524288 => Array [], + 1048576 => Array [], + 2097152 => Array [], + 4194304 => Array [], + 8388608 => Array [], + 16777216 => Array [], + 33554432 => Array [], + 67108864 => Array [], + 134217728 => Array [], + 268435456 => Array [], + 536870912 => Array [], + 1073741824 => Array [], + }, + "nativeEvents": Array [], + "networkMeasures": Array [], + "otherUserTimingMarks": Array [], + "reactVersion": "", + "schedulingEvents": Array [ + Object { + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "schedule-render", + "warning": null, + }, + Object { + "componentName": "App", + "lanes": "0b0000000000000000000000000010000", + "timestamp": 10, + "type": "schedule-state-update", + "warning": null, + }, + ], + "snapshotHeight": 0, + "snapshots": Array [], + "startTime": -10, + "suspenseEvents": Array [], + "thrownErrors": Array [], + } + `); + }); }); }); From 3721c9b58f815cbf1d9a715f30e0697d7caa6edf Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 27 Jan 2022 13:37:35 -0500 Subject: [PATCH 6/9] Fixed logic bug in conditional performance logging --- .../src/__tests__/TimelineProfiler-test.js | 1833 ++++++++--------- .../src/backend/profilingHooks.js | 628 +++--- 2 files changed, 1212 insertions(+), 1249 deletions(-) diff --git a/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js b/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js index 5f2ad905dd23a..89cc058006289 100644 --- a/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js +++ b/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js @@ -124,155 +124,904 @@ describe('Timeline profiler', () => { setPerformanceMock(null); }); - describe('when profiling', () => { - beforeEach(() => { - utils.act(() => store.profilerStore.startProfiling()); + it('should mark sync render without suspends or state updates', () => { + renderHelper(
); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-1", + "--render-start-1", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-1", + "--layout-effects-stop", + "--commit-stop", + ] + `); + }); + + it('should mark concurrent render without suspends or state updates', () => { + renderRootHelper(
); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + ] + `); + + clearPendingMarks(); + + expect(Scheduler).toFlushUntilNextPaint([]); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); + }); + + it('should mark render yields', async () => { + function Bar() { + Scheduler.unstable_yieldValue('Bar'); + return null; + } + + function Foo() { + Scheduler.unstable_yieldValue('Foo'); + return ; + } - // Clear inital metadata marks to make tests below less noisy. - clearPendingMarks(); + React.startTransition(() => { + renderRootHelper(); }); - it('should mark sync render without suspends or state updates', () => { - renderHelper(
); + // Do one step of work. + expect(Scheduler).toFlushAndYieldThrough(['Foo']); - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-1", - "--render-start-1", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start- at filtered (:0:0)", - "--react-internal-module-stop- at filtered (:1:1)", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-1", - "--layout-effects-stop", - "--commit-stop", - ] + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-64", + "--render-start-64", + "--component-render-start-Foo", + "--component-render-stop", + "--render-yield", + ] `); - }); + }); - it('should mark concurrent render without suspends or state updates', () => { - renderRootHelper(
); + it('should mark sync render with suspense that resolves', async () => { + const fakeSuspensePromise = Promise.resolve(true); + function Example() { + throw fakeSuspensePromise; + } - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-16", - ] + renderHelper( + + + , + ); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-1", + "--render-start-1", + "--component-render-start-Example", + "--component-render-stop", + "--suspense-suspend-0-Example-mount-1-", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-1", + "--layout-effects-stop", + "--commit-stop", + ] + `); + + clearPendingMarks(); + + await fakeSuspensePromise; + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--suspense-resolved-0-Example", + ] `); + }); - clearPendingMarks(); + it('should mark sync render with suspense that rejects', async () => { + const fakeSuspensePromise = Promise.reject(new Error('error')); + function Example() { + throw fakeSuspensePromise; + } - expect(Scheduler).toFlushUntilNextPaint([]); + renderHelper( + + + , + ); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-1", + "--render-start-1", + "--component-render-start-Example", + "--component-render-stop", + "--suspense-suspend-0-Example-mount-1-", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-1", + "--layout-effects-stop", + "--commit-stop", + ] + `); + + clearPendingMarks(); + + await expect(fakeSuspensePromise).rejects.toThrow(); + expect(clearedMarks).toContain(`--suspense-rejected-0-Example`); + }); - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--render-start-16", - "--render-stop", - "--commit-start-16", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start- at filtered (:0:0)", - "--react-internal-module-stop- at filtered (:1:1)", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-16", - "--layout-effects-stop", - "--commit-stop", - ] + it('should mark concurrent render with suspense that resolves', async () => { + const fakeSuspensePromise = Promise.resolve(true); + function Example() { + throw fakeSuspensePromise; + } + + renderRootHelper( + + + , + ); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + ] `); - }); - it('should mark render yields', async () => { - function Bar() { - Scheduler.unstable_yieldValue('Bar'); + clearPendingMarks(); + + expect(Scheduler).toFlushUntilNextPaint([]); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--suspense-suspend-0-Example-mount-16-", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); + + clearPendingMarks(); + + await fakeSuspensePromise; + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--suspense-resolved-0-Example", + ] + `); + }); + + it('should mark concurrent render with suspense that rejects', async () => { + const fakeSuspensePromise = Promise.reject(new Error('error')); + function Example() { + throw fakeSuspensePromise; + } + + renderRootHelper( + + + , + ); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + ] + `); + + clearPendingMarks(); + + expect(Scheduler).toFlushUntilNextPaint([]); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--suspense-suspend-0-Example-mount-16-", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); + + clearPendingMarks(); + + await expect(fakeSuspensePromise).rejects.toThrow(); + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--suspense-rejected-0-Example", + ] + `); + }); + + it('should mark cascading class component state updates', () => { + class Example extends React.Component { + state = {didMount: false}; + componentDidMount() { + this.setState({didMount: true}); + } + render() { return null; } + } - function Foo() { - Scheduler.unstable_yieldValue('Foo'); - return ; + renderRootHelper(); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + ] + `); + + clearPendingMarks(); + + expect(Scheduler).toFlushUntilNextPaint([]); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--schedule-state-update-1-Example", + "--layout-effects-stop", + "--render-start-1", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", + "--commit-stop", + ] + `); + }); + + it('should mark cascading class component force updates', () => { + class Example extends React.Component { + componentDidMount() { + this.forceUpdate(); } + render() { + return null; + } + } - React.startTransition(() => { - renderRootHelper(); - }); + renderRootHelper(); - // Do one step of work. - expect(Scheduler).toFlushAndYieldThrough(['Foo']); + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + ] + `); - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-64", - "--render-start-64", - "--component-render-start-Foo", - "--component-render-stop", - "--render-yield", - ] + clearPendingMarks(); + + expect(Scheduler).toFlushUntilNextPaint([]); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--schedule-forced-update-1-Example", + "--layout-effects-stop", + "--render-start-1", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", + "--commit-stop", + ] + `); + }); + + it('should mark render phase state updates for class component', () => { + class Example extends React.Component { + state = {didRender: false}; + render() { + if (this.state.didRender === false) { + this.setState({didRender: true}); + } + return null; + } + } + + renderRootHelper(); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + ] `); + + clearPendingMarks(); + + let errorMessage; + spyOn(console, 'error').and.callFake(message => { + errorMessage = message; }); - it('should mark sync render with suspense that resolves', async () => { - const fakeSuspensePromise = Promise.resolve(true); - function Example() { - throw fakeSuspensePromise; + expect(Scheduler).toFlushUntilNextPaint([]); + + expect(console.error).toHaveBeenCalledTimes(1); + expect(errorMessage).toContain( + 'Cannot update during an existing state transition', + ); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--component-render-start-Example", + "--schedule-state-update-16-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); + }); + + it('should mark render phase force updates for class component', () => { + let forced = false; + class Example extends React.Component { + render() { + if (!forced) { + forced = true; + this.forceUpdate(); + } + return null; } + } - renderHelper( - - - , - ); + renderRootHelper(); - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--schedule-render-1", - "--render-start-1", - "--component-render-start-Example", - "--component-render-stop", - "--suspense-suspend-0-Example-mount-1-", - "--render-stop", - "--commit-start-1", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start- at filtered (:0:0)", - "--react-internal-module-stop- at filtered (:1:1)", - "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", - "--layout-effects-start-1", - "--layout-effects-stop", - "--commit-stop", - ] - `); + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + ] + `); - clearPendingMarks(); + clearPendingMarks(); - await fakeSuspensePromise; - expect(clearedMarks).toMatchInlineSnapshot(` - Array [ - "--suspense-resolved-0-Example", - ] - `); + let errorMessage; + spyOn(console, 'error').and.callFake(message => { + errorMessage = message; }); - it('should mark sync render with suspense that rejects', async () => { - const fakeSuspensePromise = Promise.reject(new Error('error')); - function Example() { - throw fakeSuspensePromise; + expect(Scheduler).toFlushUntilNextPaint([]); + + expect(console.error).toHaveBeenCalledTimes(1); + expect(errorMessage).toContain( + 'Cannot update during an existing state transition', + ); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--component-render-start-Example", + "--schedule-forced-update-16-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); + }); + + it('should mark cascading layout updates', () => { + function Example() { + const [didMount, setDidMount] = React.useState(false); + React.useLayoutEffect(() => { + setDidMount(true); + }, []); + return didMount; + } + + renderRootHelper(); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + ] + `); + + clearPendingMarks(); + + expect(Scheduler).toFlushUntilNextPaint([]); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--component-layout-effect-mount-start-Example", + "--schedule-state-update-1-Example", + "--component-layout-effect-mount-stop", + "--layout-effects-stop", + "--render-start-1", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", + "--commit-stop", + ] + `); + }); + + it('should mark cascading passive updates', () => { + function Example() { + const [didMount, setDidMount] = React.useState(false); + React.useEffect(() => { + setDidMount(true); + }, []); + return didMount; + } + + renderRootHelper(); + + expect(Scheduler).toFlushAndYield([]); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + "--passive-effects-start-16", + "--component-passive-effect-mount-start-Example", + "--schedule-state-update-16-Example", + "--component-passive-effect-mount-stop", + "--passive-effects-stop", + "--render-start-16", + "--component-render-start-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", + ] + `); + }); + + it('should mark render phase updates', () => { + function Example() { + const [didRender, setDidRender] = React.useState(false); + if (!didRender) { + setDidRender(true); } + return didRender; + } - renderHelper( - - - , - ); + renderRootHelper(); + + expect(Scheduler).toFlushAndYield([]); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + "--render-start-16", + "--component-render-start-Example", + "--schedule-state-update-16-Example", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--layout-effects-stop", + "--commit-stop", + ] + `); + }); + + it('should mark sync render that throws', async () => { + spyOn(console, 'error'); + + class ErrorBoundary extends React.Component { + state = {error: null}; + componentDidCatch(error) { + this.setState({error}); + } + render() { + if (this.state.error) { + return null; + } + return this.props.children; + } + } + + function ExampleThatThrows() { + throw Error('Expected error'); + } + + renderHelper( + + + , + ); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-1", + "--render-start-1", + "--component-render-start-ErrorBoundary", + "--component-render-stop", + "--component-render-start-ExampleThatThrows", + "--component-render-start-ExampleThatThrows", + "--component-render-stop", + "--error-ExampleThatThrows-mount-Expected error", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-1", + "--schedule-state-update-1-ErrorBoundary", + "--layout-effects-stop", + "--commit-stop", + "--render-start-1", + "--component-render-start-ErrorBoundary", + "--component-render-stop", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", + ] + `); + }); + + it('should mark concurrent render that throws', async () => { + spyOn(console, 'error'); + + class ErrorBoundary extends React.Component { + state = {error: null}; + componentDidCatch(error) { + this.setState({error}); + } + render() { + if (this.state.error) { + return null; + } + return this.props.children; + } + } + + function ExampleThatThrows() { + // eslint-disable-next-line no-throw-literal + throw 'Expected error'; + } + + renderRootHelper( + + + , + ); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + ] + `); + + clearPendingMarks(); + + expect(Scheduler).toFlushUntilNextPaint([]); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--render-start-16", + "--component-render-start-ErrorBoundary", + "--component-render-stop", + "--component-render-start-ExampleThatThrows", + "--component-render-start-ExampleThatThrows", + "--component-render-stop", + "--error-ExampleThatThrows-mount-Expected error", + "--render-stop", + "--render-start-16", + "--component-render-start-ErrorBoundary", + "--component-render-stop", + "--component-render-start-ExampleThatThrows", + "--component-render-start-ExampleThatThrows", + "--component-render-stop", + "--error-ExampleThatThrows-mount-Expected error", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--schedule-state-update-1-ErrorBoundary", + "--layout-effects-stop", + "--render-start-1", + "--component-render-start-ErrorBoundary", + "--component-render-stop", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--commit-stop", + "--commit-stop", + ] + `); + }); + + it('should mark passive and layout effects', async () => { + function ComponentWithEffects() { + React.useLayoutEffect(() => { + Scheduler.unstable_yieldValue('layout 1 mount'); + return () => { + Scheduler.unstable_yieldValue('layout 1 unmount'); + }; + }, []); + + React.useEffect(() => { + Scheduler.unstable_yieldValue('passive 1 mount'); + return () => { + Scheduler.unstable_yieldValue('passive 1 unmount'); + }; + }, []); + + React.useLayoutEffect(() => { + Scheduler.unstable_yieldValue('layout 2 mount'); + return () => { + Scheduler.unstable_yieldValue('layout 2 unmount'); + }; + }, []); + + React.useEffect(() => { + Scheduler.unstable_yieldValue('passive 2 mount'); + return () => { + Scheduler.unstable_yieldValue('passive 2 unmount'); + }; + }, []); + + React.useEffect(() => { + Scheduler.unstable_yieldValue('passive 3 mount'); + return () => { + Scheduler.unstable_yieldValue('passive 3 unmount'); + }; + }, []); + + return null; + } + + const unmount = renderRootHelper(); + + expect(Scheduler).toFlushUntilNextPaint([ + 'layout 1 mount', + 'layout 2 mount', + ]); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + "--render-start-16", + "--component-render-start-ComponentWithEffects", + "--component-render-stop", + "--render-stop", + "--commit-start-16", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--layout-effects-start-16", + "--component-layout-effect-mount-start-ComponentWithEffects", + "--component-layout-effect-mount-stop", + "--component-layout-effect-mount-start-ComponentWithEffects", + "--component-layout-effect-mount-stop", + "--layout-effects-stop", + "--commit-stop", + ] + `); + + clearPendingMarks(); + + expect(Scheduler).toFlushAndYield([ + 'passive 1 mount', + 'passive 2 mount', + 'passive 3 mount', + ]); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--passive-effects-start-16", + "--component-passive-effect-mount-start-ComponentWithEffects", + "--component-passive-effect-mount-stop", + "--component-passive-effect-mount-start-ComponentWithEffects", + "--component-passive-effect-mount-stop", + "--component-passive-effect-mount-start-ComponentWithEffects", + "--component-passive-effect-mount-stop", + "--passive-effects-stop", + ] + `); + + clearPendingMarks(); + + expect(Scheduler).toFlushAndYield([]); + + unmount(); + + expect(Scheduler).toHaveYielded([ + 'layout 1 unmount', + 'layout 2 unmount', + 'passive 1 unmount', + 'passive 2 unmount', + 'passive 3 unmount', + ]); + + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-1", + "--render-start-1", + "--render-stop", + "--commit-start-1", + "--react-version-", + "--profiler-version-1", + "--react-internal-module-start- at filtered (:0:0)", + "--react-internal-module-stop- at filtered (:1:1)", + "--react-lane-labels-Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", + "--component-layout-effect-unmount-start-ComponentWithEffects", + "--component-layout-effect-unmount-stop", + "--component-layout-effect-unmount-start-ComponentWithEffects", + "--component-layout-effect-unmount-stop", + "--layout-effects-start-1", + "--layout-effects-stop", + "--passive-effects-start-1", + "--component-passive-effect-unmount-start-ComponentWithEffects", + "--component-passive-effect-unmount-stop", + "--component-passive-effect-unmount-start-ComponentWithEffects", + "--component-passive-effect-unmount-stop", + "--component-passive-effect-unmount-start-ComponentWithEffects", + "--component-passive-effect-unmount-stop", + "--passive-effects-stop", + "--commit-stop", + ] + `); + }); + + describe('lane labels', () => { + it('regression test SyncLane', () => { + renderHelper(
); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--schedule-render-1", "--render-start-1", - "--component-render-start-Example", - "--component-render-stop", - "--suspense-suspend-0-Example-mount-1-", "--render-stop", "--commit-start-1", "--react-version-", @@ -285,862 +1034,96 @@ describe('Timeline profiler', () => { "--commit-stop", ] `); + }); - clearPendingMarks(); - - await expect(fakeSuspensePromise).rejects.toThrow(); - expect(clearedMarks).toContain(`--suspense-rejected-0-Example`); + it('regression test DefaultLane', () => { + renderRootHelper(
); + expect(clearedMarks).toMatchInlineSnapshot(` + Array [ + "--schedule-render-16", + ] + `); }); - it('should mark concurrent render with suspense that resolves', async () => { - const fakeSuspensePromise = Promise.resolve(true); - function Example() { - throw fakeSuspensePromise; + it('regression test InputDiscreteLane', async () => { + const targetRef = React.createRef(null); + + function App() { + const [count, setCount] = React.useState(0); + const handleClick = () => { + setCount(count + 1); + }; + return