/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ 'use strict'; import {normalizeCodeLocInfo} from './utils'; describe('Timeline profiler', () => { let React; let ReactDOMClient; 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 = 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'); store = global.store; }); 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 '--react-internal-module-start- at filtered (:0:0)'; } else if (markName.startsWith('--react-internal-module-stop')) { return '--react-internal-module-stop- at filtered (:1:1)'; } else if (markName.startsWith('--react-version')) { return '--react-version-'; } 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++; } }, }; } function clearPendingMarks() { clearedMarks.splice(0); } function dispatchAndSetCurrentEvent(element, event) { try { window.event = event; element.dispatchEvent(event); } finally { window.event = undefined; } } beforeEach(() => { setPerformanceMock = require('react-devtools-shared/src/backend/profilingHooks') .setPerformanceMock_ONLY_FOR_TESTING; setPerformanceMock(createUserTimingPolyfill()); }); afterEach(() => { // 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(
); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--schedule-render-2", "--render-start-2", "--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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-2", "--layout-effects-stop", "--commit-stop", ] `); }); it('should mark concurrent render without suspends or state updates', () => { renderRootHelper(
); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--schedule-render-32", ] `); clearPendingMarks(); expect(Scheduler).toFlushUntilNextPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--render-start-32", "--render-stop", "--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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-32", "--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 ; } React.startTransition(() => { renderRootHelper(); }); // Do one step of work. expect(Scheduler).toFlushAndYieldThrough(['Foo']); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--schedule-render-128", "--render-start-128", "--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; } renderHelper( , ); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--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-", "--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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-2", "--layout-effects-stop", "--commit-stop", ] `); clearPendingMarks(); await fakeSuspensePromise; expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--suspense-resolved-0-Example", ] `); }); it('should mark sync render with suspense that rejects', async () => { const fakeSuspensePromise = Promise.reject(new Error('error')); function Example() { throw fakeSuspensePromise; } renderHelper( , ); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--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-", "--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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-2", "--layout-effects-stop", "--commit-stop", ] `); clearPendingMarks(); await expect(fakeSuspensePromise).rejects.toThrow(); expect(clearedMarks).toContain(`--suspense-rejected-0-Example`); }); 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-32", ] `); clearPendingMarks(); expect(Scheduler).toFlushUntilNextPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--render-start-32", "--component-render-start-Example", "--component-render-stop", "--suspense-suspend-0-Example-mount-32-", "--render-stop", "--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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-32", "--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-32", ] `); clearPendingMarks(); expect(Scheduler).toFlushUntilNextPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--render-start-32", "--component-render-start-Example", "--component-render-stop", "--suspense-suspend-0-Example-mount-32-", "--render-stop", "--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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-32", "--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; } } renderRootHelper(); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--schedule-render-32", ] `); clearPendingMarks(); expect(Scheduler).toFlushUntilNextPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--render-start-32", "--component-render-start-Example", "--component-render-stop", "--render-stop", "--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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-32", "--schedule-state-update-2-Example", "--layout-effects-stop", "--render-start-2", "--component-render-start-Example", "--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,Transition,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; } } renderRootHelper(); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--schedule-render-32", ] `); clearPendingMarks(); expect(Scheduler).toFlushUntilNextPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--render-start-32", "--component-render-start-Example", "--component-render-stop", "--render-stop", "--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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-32", "--schedule-forced-update-2-Example", "--layout-effects-stop", "--render-start-2", "--component-render-start-Example", "--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,Transition,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-32", ] `); clearPendingMarks(); 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-32", "--component-render-start-Example", "--schedule-state-update-32-Example", "--component-render-stop", "--render-stop", "--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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-32", "--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; } } renderRootHelper(); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--schedule-render-32", ] `); clearPendingMarks(); 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-32", "--component-render-start-Example", "--schedule-forced-update-32-Example", "--component-render-stop", "--render-stop", "--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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-32", "--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-32", ] `); clearPendingMarks(); expect(Scheduler).toFlushUntilNextPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--render-start-32", "--component-render-start-Example", "--component-render-stop", "--render-stop", "--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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-32", "--component-layout-effect-mount-start-Example", "--schedule-state-update-2-Example", "--component-layout-effect-mount-stop", "--layout-effects-stop", "--render-start-2", "--component-render-start-Example", "--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,Transition,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-32", "--render-start-32", "--component-render-start-Example", "--component-render-stop", "--render-stop", "--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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-32", "--layout-effects-stop", "--commit-stop", "--passive-effects-start-32", "--component-passive-effect-mount-start-Example", "--schedule-state-update-32-Example", "--component-passive-effect-mount-stop", "--passive-effects-stop", "--render-start-32", "--component-render-start-Example", "--component-render-stop", "--render-stop", "--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,Transition,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; } renderRootHelper(); expect(Scheduler).toFlushAndYield([]); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--schedule-render-32", "--render-start-32", "--component-render-start-Example", "--schedule-state-update-32-Example", "--component-render-stop", "--render-stop", "--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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-32", "--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-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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--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,Transition,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-32", ] `); clearPendingMarks(); expect(Scheduler).toFlushUntilNextPaint([]); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--render-start-32", "--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-32", "--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-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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-32", "--schedule-state-update-2-ErrorBoundary", "--layout-effects-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,Transition,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-32", "--render-start-32", "--component-render-start-ComponentWithEffects", "--component-render-stop", "--render-stop", "--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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-32", "--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-32", "--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-2", "--render-start-2", "--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,Transition,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-2", "--layout-effects-stop", "--passive-effects-start-2", "--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-2", "--render-start-2", "--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,Transition,Retry,Retry,Retry,Retry,SelectiveHydration,IdleHydration,Idle,Offscreen", "--layout-effects-start-2", "--layout-effects-stop", "--commit-stop", ] `); }); it('regression test DefaultLane', () => { renderRootHelper(
); expect(clearedMarks).toMatchInlineSnapshot(` Array [ "--schedule-render-32", ] `); }); it('regression test InputDiscreteLane', async () => { const targetRef = React.createRef(null); function App() { const [count, setCount] = React.useState(0); const handleClick = () => { setCount(count + 1); }; return