From f251341a59bf256b654a97545d6438dafadfb539 Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Fri, 2 Feb 2024 18:09:09 +0000 Subject: [PATCH] chore: use versioned render in TimelineProfiler test and gate some for legacy rendering --- .../src/__tests__/TimelineProfiler-test.js | 2180 +++++++++-------- 1 file changed, 1116 insertions(+), 1064 deletions(-) diff --git a/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js b/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js index 23ad7f657ba33..88aed0ee63250 100644 --- a/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js +++ b/packages/react-devtools-shared/src/__tests__/TimelineProfiler-test.js @@ -9,51 +9,25 @@ 'use strict'; -import {normalizeCodeLocInfo} from './utils'; +import { + getLegacyRenderImplementation, + getModernRenderImplementation, + normalizeCodeLocInfo, +} from './utils'; describe('Timeline profiler', () => { let React; - let ReactDOMClient; let Scheduler; - let renderHelper; - let renderRootHelper; let store; - let unmountFns; let utils; - let waitFor; - let waitForAll; - let waitForPaint; - let assertLog; 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 = ReactDOMClient.createRoot(container); - root.render(element); - const unmountFn = () => root.unmount(); - unmountFns.push(unmountFn); - return unmountFn; - }; - React = require('react'); - ReactDOMClient = require('react-dom/client'); Scheduler = require('scheduler'); - const InternalTestUtils = require('internal-test-utils'); - waitFor = InternalTestUtils.waitFor; - waitForAll = InternalTestUtils.waitForAll; - waitForPaint = InternalTestUtils.waitForPaint; - assertLog = InternalTestUtils.assertLog; - store = global.store; }); @@ -135,18 +109,57 @@ describe('Timeline profiler', () => { // Verify all logged marks also get cleared. expect(marks).toHaveLength(0); - unmountFns.forEach(unmountFn => unmountFn()); - setPerformanceMock(null); }); - it('should mark sync render without suspends or state updates', () => { - renderHelper(
); + describe('with legacy render', () => { + const {render: legacyRender} = getLegacyRenderImplementation(); + + // @reactVersion <= 18.2 + // @reactVersion >= 18.0 + it('should mark sync render without suspends or state updates', () => { + legacyRender(
); + + expect(clearedMarks).toMatchInlineSnapshot(` + [ + "--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", + ] + `); + }); + + // TODO(hoxyq): investigate why running this test with React 18 fails + // @reactVersion <= 18.2 + // @reactVersion >= 18.0 + xit('should mark sync render with suspense that resolves', async () => { + const fakeSuspensePromise = Promise.resolve(true); + function Example() { + throw fakeSuspensePromise; + } + + legacyRender( + + + , + ); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--schedule-render-2", "--render-start-2", + "--component-render-start-Example", + "--component-render-stop", + "--suspense-suspend-0-Example-mount-2-", "--render-stop", "--commit-start-2", "--react-version-", @@ -159,173 +172,224 @@ describe('Timeline profiler', () => { "--commit-stop", ] `); - }); - it('should mark concurrent render without suspends or state updates', async () => { - renderRootHelper(
); + clearPendingMarks(); - expect(clearedMarks).toMatchInlineSnapshot(` - [ - "--schedule-render-32", - ] - `); + await fakeSuspensePromise; + expect(clearedMarks).toMatchInlineSnapshot(` + [ + "--suspense-resolved-0-Example", + ] + `); + }); - clearPendingMarks(); + // TODO(hoxyq): investigate why running this test with React 18 fails + // @reactVersion <= 18.2 + // @reactVersion >= 18.0 + xit('should mark sync render with suspense that rejects', async () => { + const fakeSuspensePromise = Promise.reject(new Error('error')); + function Example() { + throw fakeSuspensePromise; + } - await waitForPaint([]); + legacyRender( + + + , + ); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ - "--render-start-32", + "--schedule-render-2", + "--render-start-2", + "--component-render-start-Example", + "--component-render-stop", + "--suspense-suspend-0-Example-mount-2-", "--render-stop", - "--commit-start-32", + "--commit-start-2", "--react-version-", "--profiler-version-1", "--react-internal-module-start- at filtered (:0:0)", "--react-internal-module-stop- at filtered (:1:1)", "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred", - "--layout-effects-start-32", + "--layout-effects-start-2", "--layout-effects-stop", "--commit-stop", ] `); - }); - - it('should mark render yields', async () => { - function Bar() { - Scheduler.log('Bar'); - return null; - } - function Foo() { - Scheduler.log('Foo'); - return ; - } + clearPendingMarks(); - React.startTransition(() => { - renderRootHelper(); + await expect(fakeSuspensePromise).rejects.toThrow(); + expect(clearedMarks).toContain(`--suspense-rejected-0-Example`); }); - await waitFor(['Foo']); + // @reactVersion <= 18.2 + // @reactVersion >= 18.0 + it('should mark sync render that throws', async () => { + jest.spyOn(console, 'error').mockImplementation(() => {}); - expect(clearedMarks).toMatchInlineSnapshot(` - [ - "--schedule-render-128", - "--render-start-128", - "--component-render-start-Foo", - "--component-render-stop", - "--render-yield", - ] - `); - }); + class ErrorBoundary extends React.Component { + state = {error: null}; + componentDidCatch(error) { + this.setState({error}); + } + render() { + if (this.state.error) { + return null; + } + return this.props.children; + } + } - it('should mark sync render with suspense that resolves', async () => { - const fakeSuspensePromise = Promise.resolve(true); - function Example() { - throw fakeSuspensePromise; - } + function ExampleThatThrows() { + throw Error('Expected error'); + } - renderHelper( - - - , - ); + legacyRender( + + + , + ); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ - "--schedule-render-2", - "--render-start-2", - "--component-render-start-Example", + "--schedule-render-1", + "--render-start-1", + "--component-render-start-ErrorBoundary", "--component-render-stop", - "--suspense-suspend-0-Example-mount-2-", + "--component-render-start-ExampleThatThrows", + "--component-render-start-ExampleThatThrows", + "--component-render-stop", + "--error-ExampleThatThrows-mount-Expected error", "--render-stop", - "--commit-start-2", + "--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-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred", - "--layout-effects-start-2", + "--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", ] `); + }); + }); - clearPendingMarks(); + describe('with createRoot', () => { + let waitFor; + let waitForAll; + let waitForPaint; + let assertLog; - await fakeSuspensePromise; - expect(clearedMarks).toMatchInlineSnapshot(` - [ - "--suspense-resolved-0-Example", - ] - `); - }); + beforeEach(() => { + const InternalTestUtils = require('internal-test-utils'); + waitFor = InternalTestUtils.waitFor; + waitForAll = InternalTestUtils.waitForAll; + waitForPaint = InternalTestUtils.waitForPaint; + assertLog = InternalTestUtils.assertLog; + }); - it('should mark sync render with suspense that rejects', async () => { - const fakeSuspensePromise = Promise.reject(new Error('error')); - function Example() { - throw fakeSuspensePromise; - } + const {render: modernRender} = getModernRenderImplementation(); - renderHelper( - - - , - ); + it('should mark concurrent render without suspends or state updates', async () => { + modernRender(
); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ - "--schedule-render-2", - "--render-start-2", - "--component-render-start-Example", - "--component-render-stop", - "--suspense-suspend-0-Example-mount-2-", + "--schedule-render-32", + ] + `); + + clearPendingMarks(); + + await waitForPaint([]); + + expect(clearedMarks).toMatchInlineSnapshot(` + [ + "--render-start-32", "--render-stop", - "--commit-start-2", + "--commit-start-32", "--react-version-", "--profiler-version-1", "--react-internal-module-start- at filtered (:0:0)", "--react-internal-module-stop- at filtered (:1:1)", "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred", - "--layout-effects-start-2", + "--layout-effects-start-32", "--layout-effects-stop", "--commit-stop", ] `); + }); - clearPendingMarks(); + it('should mark render yields', async () => { + function Bar() { + Scheduler.log('Bar'); + return null; + } - await expect(fakeSuspensePromise).rejects.toThrow(); - expect(clearedMarks).toContain(`--suspense-rejected-0-Example`); - }); + function Foo() { + Scheduler.log('Foo'); + return ; + } + + React.startTransition(() => { + modernRender(); + }); - it('should mark concurrent render with suspense that resolves', async () => { - let resolveFakePromise; - const fakeSuspensePromise = new Promise( - resolve => (resolveFakePromise = resolve), - ); + await waitFor(['Foo']); - function Example() { - throw fakeSuspensePromise; - } + expect(clearedMarks).toMatchInlineSnapshot(` + [ + "--schedule-render-128", + "--render-start-128", + "--component-render-start-Foo", + "--component-render-stop", + "--render-yield", + ] + `); + }); + + it('should mark concurrent render with suspense that resolves', async () => { + let resolveFakePromise; + const fakeSuspensePromise = new Promise( + resolve => (resolveFakePromise = resolve), + ); + + function Example() { + throw fakeSuspensePromise; + } - renderRootHelper( - - - , - ); + modernRender( + + + , + ); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--schedule-render-32", ] `); - clearPendingMarks(); + clearPendingMarks(); - await waitForPaint([]); + await waitForPaint([]); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--render-start-32", "--component-render-start-Example", @@ -344,43 +408,43 @@ describe('Timeline profiler', () => { ] `); - clearPendingMarks(); + clearPendingMarks(); - await resolveFakePromise(); - expect(clearedMarks).toMatchInlineSnapshot(` + await resolveFakePromise(); + expect(clearedMarks).toMatchInlineSnapshot(` [ "--suspense-resolved-0-Example", ] `); - }); + }); - it('should mark concurrent render with suspense that rejects', async () => { - let rejectFakePromise; - const fakeSuspensePromise = new Promise( - (_, reject) => (rejectFakePromise = reject), - ); + it('should mark concurrent render with suspense that rejects', async () => { + let rejectFakePromise; + const fakeSuspensePromise = new Promise( + (_, reject) => (rejectFakePromise = reject), + ); - function Example() { - throw fakeSuspensePromise; - } + function Example() { + throw fakeSuspensePromise; + } - renderRootHelper( - - - , - ); + modernRender( + + + , + ); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--schedule-render-32", ] `); - clearPendingMarks(); + clearPendingMarks(); - await waitForPaint([]); + await waitForPaint([]); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--render-start-32", "--component-render-start-Example", @@ -399,43 +463,43 @@ describe('Timeline profiler', () => { ] `); - clearPendingMarks(); + clearPendingMarks(); - await expect(() => { - rejectFakePromise(new Error('error')); - return fakeSuspensePromise; - }).rejects.toThrow(); - expect(clearedMarks).toMatchInlineSnapshot(` + await expect(() => { + rejectFakePromise(new Error('error')); + return fakeSuspensePromise; + }).rejects.toThrow(); + expect(clearedMarks).toMatchInlineSnapshot(` [ "--suspense-rejected-0-Example", ] `); - }); + }); - it('should mark cascading class component state updates', async () => { - class Example extends React.Component { - state = {didMount: false}; - componentDidMount() { - this.setState({didMount: true}); - } - render() { - return null; + it('should mark cascading class component state updates', async () => { + class Example extends React.Component { + state = {didMount: false}; + componentDidMount() { + this.setState({didMount: true}); + } + render() { + return null; + } } - } - renderRootHelper(); + modernRender(); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--schedule-render-32", ] `); - clearPendingMarks(); + clearPendingMarks(); - await waitForPaint([]); + await waitForPaint([]); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--render-start-32", "--component-render-start-Example", @@ -464,31 +528,31 @@ describe('Timeline profiler', () => { "--commit-stop", ] `); - }); + }); - it('should mark cascading class component force updates', async () => { - class Example extends React.Component { - componentDidMount() { - this.forceUpdate(); - } - render() { - return null; + it('should mark cascading class component force updates', async () => { + class Example extends React.Component { + componentDidMount() { + this.forceUpdate(); + } + render() { + return null; + } } - } - renderRootHelper(); + modernRender(); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--schedule-render-32", ] `); - clearPendingMarks(); + clearPendingMarks(); - await waitForPaint([]); + await waitForPaint([]); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--render-start-32", "--component-render-start-Example", @@ -517,42 +581,42 @@ describe('Timeline profiler', () => { "--commit-stop", ] `); - }); + }); - it('should mark render phase state updates for class component', async () => { - class Example extends React.Component { - state = {didRender: false}; - render() { - if (this.state.didRender === false) { - this.setState({didRender: true}); + it('should mark render phase state updates for class component', async () => { + class Example extends React.Component { + state = {didRender: false}; + render() { + if (this.state.didRender === false) { + this.setState({didRender: true}); + } + return null; } - return null; } - } - renderRootHelper(); + modernRender(); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--schedule-render-32", ] `); - clearPendingMarks(); + clearPendingMarks(); - let errorMessage; - jest.spyOn(console, 'error').mockImplementation(message => { - errorMessage = message; - }); + let errorMessage; + jest.spyOn(console, 'error').mockImplementation(message => { + errorMessage = message; + }); - await waitForPaint([]); + await waitForPaint([]); - expect(console.error).toHaveBeenCalledTimes(1); - expect(errorMessage).toContain( - 'Cannot update during an existing state transition', - ); + expect(console.error).toHaveBeenCalledTimes(1); + expect(errorMessage).toContain( + 'Cannot update during an existing state transition', + ); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--render-start-32", "--component-render-start-Example", @@ -570,43 +634,43 @@ describe('Timeline profiler', () => { "--commit-stop", ] `); - }); + }); - it('should mark render phase force updates for class component', async () => { - let forced = false; - class Example extends React.Component { - render() { - if (!forced) { - forced = true; - this.forceUpdate(); - } - return null; + it('should mark render phase force updates for class component', async () => { + let forced = false; + class Example extends React.Component { + render() { + if (!forced) { + forced = true; + this.forceUpdate(); + } + return null; + } } - } - renderRootHelper(); + modernRender(); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--schedule-render-32", ] `); - clearPendingMarks(); + clearPendingMarks(); - let errorMessage; - jest.spyOn(console, 'error').mockImplementation(message => { - errorMessage = message; - }); + let errorMessage; + jest.spyOn(console, 'error').mockImplementation(message => { + errorMessage = message; + }); - await waitForPaint([]); + await waitForPaint([]); - expect(console.error).toHaveBeenCalledTimes(1); - expect(errorMessage).toContain( - 'Cannot update during an existing state transition', - ); + expect(console.error).toHaveBeenCalledTimes(1); + expect(errorMessage).toContain( + 'Cannot update during an existing state transition', + ); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--render-start-32", "--component-render-start-Example", @@ -624,30 +688,30 @@ describe('Timeline profiler', () => { "--commit-stop", ] `); - }); + }); - it('should mark cascading layout updates', async () => { - function Example() { - const [didMount, setDidMount] = React.useState(false); - React.useLayoutEffect(() => { - setDidMount(true); - }, []); - return didMount; - } + it('should mark cascading layout updates', async () => { + function Example() { + const [didMount, setDidMount] = React.useState(false); + React.useLayoutEffect(() => { + setDidMount(true); + }, []); + return didMount; + } - renderRootHelper(); + modernRender(); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--schedule-render-32", ] `); - clearPendingMarks(); + clearPendingMarks(); - await waitForPaint([]); + await waitForPaint([]); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--render-start-32", "--component-render-start-Example", @@ -678,22 +742,22 @@ describe('Timeline profiler', () => { "--commit-stop", ] `); - }); + }); - it('should mark cascading passive updates', async () => { - function Example() { - const [didMount, setDidMount] = React.useState(false); - React.useEffect(() => { - setDidMount(true); - }, []); - return didMount; - } + it('should mark cascading passive updates', async () => { + function Example() { + const [didMount, setDidMount] = React.useState(false); + React.useEffect(() => { + setDidMount(true); + }, []); + return didMount; + } - renderRootHelper(); + modernRender(); - await waitForAll([]); + await waitForAll([]); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--schedule-render-32", "--render-start-32", @@ -727,22 +791,22 @@ describe('Timeline profiler', () => { "--commit-stop", ] `); - }); + }); - it('should mark render phase updates', async () => { - function Example() { - const [didRender, setDidRender] = React.useState(false); - if (!didRender) { - setDidRender(true); + it('should mark render phase updates', async () => { + function Example() { + const [didRender, setDidRender] = React.useState(false); + if (!didRender) { + setDidRender(true); + } + return didRender; } - return didRender; - } - renderRootHelper(); + modernRender(); - await waitForAll([]); + await waitForAll([]); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--schedule-render-32", "--render-start-32", @@ -761,108 +825,46 @@ describe('Timeline profiler', () => { "--commit-stop", ] `); - }); + }); - it('should mark sync render that throws', async () => { - jest.spyOn(console, 'error').mockImplementation(() => {}); + it('should mark concurrent render that throws', async () => { + jest.spyOn(console, 'error').mockImplementation(() => {}); - class ErrorBoundary extends React.Component { - state = {error: null}; - componentDidCatch(error) { - this.setState({error}); - } - render() { - if (this.state.error) { - return null; + class ErrorBoundary extends React.Component { + state = {error: null}; + componentDidCatch(error) { + this.setState({error}); } - return this.props.children; - } - } - - function ExampleThatThrows() { - throw Error('Expected error'); - } - - renderHelper( - - - , - ); - - expect(clearedMarks).toMatchInlineSnapshot(` - [ - "--schedule-render-2", - "--render-start-2", - "--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-2", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start- at filtered (:0:0)", - "--react-internal-module-stop- at filtered (:1:1)", - "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred", - "--layout-effects-start-2", - "--schedule-state-update-2-ErrorBoundary", - "--layout-effects-stop", - "--commit-stop", - "--render-start-2", - "--component-render-start-ErrorBoundary", - "--component-render-stop", - "--render-stop", - "--commit-start-2", - "--react-version-", - "--profiler-version-1", - "--react-internal-module-start- at filtered (:0:0)", - "--react-internal-module-stop- at filtered (:1:1)", - "--react-lane-labels-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred", - "--commit-stop", - ] - `); - }); - - it('should mark concurrent render that throws', async () => { - jest.spyOn(console, 'error').mockImplementation(() => {}); - - class ErrorBoundary extends React.Component { - state = {error: null}; - componentDidCatch(error) { - this.setState({error}); - } - render() { - if (this.state.error) { - return null; + render() { + if (this.state.error) { + return null; + } + return this.props.children; } - return this.props.children; } - } - function ExampleThatThrows() { - // eslint-disable-next-line no-throw-literal - throw 'Expected error'; - } + function ExampleThatThrows() { + // eslint-disable-next-line no-throw-literal + throw 'Expected error'; + } - renderRootHelper( - - - , - ); + modernRender( + + + , + ); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--schedule-render-32", ] `); - clearPendingMarks(); + clearPendingMarks(); - await waitForPaint([]); + await waitForPaint([]); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--render-start-32", "--component-render-start-ErrorBoundary", @@ -903,53 +905,53 @@ describe('Timeline profiler', () => { "--commit-stop", ] `); - }); + }); - it('should mark passive and layout effects', async () => { - function ComponentWithEffects() { - React.useLayoutEffect(() => { - Scheduler.log('layout 1 mount'); - return () => { - Scheduler.log('layout 1 unmount'); - }; - }, []); - - React.useEffect(() => { - Scheduler.log('passive 1 mount'); - return () => { - Scheduler.log('passive 1 unmount'); - }; - }, []); - - React.useLayoutEffect(() => { - Scheduler.log('layout 2 mount'); - return () => { - Scheduler.log('layout 2 unmount'); - }; - }, []); - - React.useEffect(() => { - Scheduler.log('passive 2 mount'); - return () => { - Scheduler.log('passive 2 unmount'); - }; - }, []); - - React.useEffect(() => { - Scheduler.log('passive 3 mount'); - return () => { - Scheduler.log('passive 3 unmount'); - }; - }, []); + it('should mark passive and layout effects', async () => { + function ComponentWithEffects() { + React.useLayoutEffect(() => { + Scheduler.log('layout 1 mount'); + return () => { + Scheduler.log('layout 1 unmount'); + }; + }, []); - return null; - } + React.useEffect(() => { + Scheduler.log('passive 1 mount'); + return () => { + Scheduler.log('passive 1 unmount'); + }; + }, []); + + React.useLayoutEffect(() => { + Scheduler.log('layout 2 mount'); + return () => { + Scheduler.log('layout 2 unmount'); + }; + }, []); + + React.useEffect(() => { + Scheduler.log('passive 2 mount'); + return () => { + Scheduler.log('passive 2 unmount'); + }; + }, []); + + React.useEffect(() => { + Scheduler.log('passive 3 mount'); + return () => { + Scheduler.log('passive 3 unmount'); + }; + }, []); + + return null; + } - const unmount = renderRootHelper(); + const unmount = modernRender(); - await waitForPaint(['layout 1 mount', 'layout 2 mount']); + await waitForPaint(['layout 1 mount', 'layout 2 mount']); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--schedule-render-32", "--render-start-32", @@ -972,15 +974,15 @@ describe('Timeline profiler', () => { ] `); - clearPendingMarks(); + clearPendingMarks(); - await waitForAll([ - 'passive 1 mount', - 'passive 2 mount', - 'passive 3 mount', - ]); + await waitForAll([ + 'passive 1 mount', + 'passive 2 mount', + 'passive 3 mount', + ]); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--passive-effects-start-32", "--component-passive-effect-mount-start-ComponentWithEffects", @@ -993,21 +995,21 @@ describe('Timeline profiler', () => { ] `); - clearPendingMarks(); + clearPendingMarks(); - await waitForAll([]); + await waitForAll([]); - unmount(); + unmount(); - assertLog([ - 'layout 1 unmount', - 'layout 2 unmount', - 'passive 1 unmount', - 'passive 2 unmount', - 'passive 3 unmount', - ]); + assertLog([ + 'layout 1 unmount', + 'layout 2 unmount', + 'passive 1 unmount', + 'passive 2 unmount', + 'passive 3 unmount', + ]); - expect(clearedMarks).toMatchInlineSnapshot(` + expect(clearedMarks).toMatchInlineSnapshot(` [ "--schedule-render-2", "--render-start-2", @@ -1035,61 +1037,84 @@ describe('Timeline profiler', () => { "--commit-stop", ] `); + }); }); describe('lane labels', () => { - it('regression test SyncLane', () => { - renderHelper(
); + describe('with legacy render', () => { + const {render: legacyRender} = getLegacyRenderImplementation(); - expect(clearedMarks).toMatchInlineSnapshot(` + // @reactVersion <= 18.2 + // @reactVersion >= 18.0 + it('regression test SyncLane', () => { + legacyRender(
); + + expect(clearedMarks).toMatchInlineSnapshot(` [ - "--schedule-render-2", - "--render-start-2", + "--schedule-render-1", + "--render-start-1", "--render-stop", - "--commit-start-2", + "--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-SyncHydrationLane,Sync,InputContinuousHydration,InputContinuous,DefaultHydration,Default,TransitionHydration,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen,Deferred", - "--layout-effects-start-2", + "--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(` + describe('with createRoot()', () => { + let waitFor; + let waitForAll; + let waitForPaint; + let assertLog; + + beforeEach(() => { + const InternalTestUtils = require('internal-test-utils'); + waitFor = InternalTestUtils.waitFor; + waitForAll = InternalTestUtils.waitForAll; + waitForPaint = InternalTestUtils.waitForPaint; + assertLog = InternalTestUtils.assertLog; + }); + + const {render: modernRender} = getModernRenderImplementation(); + + it('regression test DefaultLane', () => { + modernRender(
); + expect(clearedMarks).toMatchInlineSnapshot(` [ "--schedule-render-32", ] `); - }); + }); - it('regression test InputDiscreteLane', async () => { - const targetRef = React.createRef(null); + it('regression test InputDiscreteLane', async () => { + const targetRef = React.createRef(null); - function App() { - const [count, setCount] = React.useState(0); - const handleClick = () => { - setCount(count + 1); - }; - return