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