UNPKG

@callstack/reassure-measure

Version:

Performance measurement library for React and React Native

101 lines (99 loc) 3.64 kB
import * as React from 'react'; import * as logger from '@callstack/reassure-logger'; import { config } from './config'; import { processRunResults } from './measure-helpers'; import { showFlagsOutputIfNeeded, writeTestStats } from './output'; import { applyRenderPolyfills, revertRenderPolyfills } from './polyfills'; import { detectRedundantUpdates } from './redundant-renders'; import { resolveTestingLibrary, getTestingLibrary } from './testing-library'; logger.configure({ verbose: process.env.REASSURE_VERBOSE === 'true' || process.env.REASSURE_VERBOSE === '1', silent: process.env.REASSURE_SILENT === 'true' || process.env.REASSURE_SILENT === '1' }); export async function measureRenders(ui, options) { const stats = await measureRendersInternal(ui, options); if (options?.writeFile !== false) { await writeTestStats(stats, 'render'); } return stats; } /** * @deprecated The `measurePerformance` function has been renamed to `measureRenders`. The `measurePerformance` alias is now deprecated and will be removed in future releases. */ export async function measurePerformance(ui, options) { logger.warnOnce('The `measurePerformance` function has been renamed to `measureRenders`.\n\nThe `measurePerformance` alias is now deprecated and will be removed in future releases.'); return await measureRenders(ui, options); } async function measureRendersInternal(ui, options) { const runs = options?.runs ?? config.runs; const scenario = options?.scenario; const warmupRuns = options?.warmupRuns ?? config.warmupRuns; const removeOutliers = options?.removeOutliers ?? config.removeOutliers; const { render, cleanup } = resolveTestingLibrary(); const testingLibrary = getTestingLibrary(); showFlagsOutputIfNeeded(); applyRenderPolyfills(); const runResults = []; const renderJsonTrees = []; let initialRenderCount = 0; for (let iteration = 0; iteration < runs + warmupRuns; iteration += 1) { await options?.beforeEach?.(); let duration = 0; let count = 0; let renderResult = null; const captureRenderDetails = () => { // We capture render details only on the first run if (iteration !== 0) { return; } // Initial render did not finish yet, so there is no "render" result yet and we cannot analyze the element tree. if (renderResult == null) { initialRenderCount += 1; return; } if (testingLibrary === 'react-native') { renderJsonTrees.push(renderResult.toJSON()); } }; const handleRender = (_id, _phase, actualDuration) => { duration += actualDuration; count += 1; captureRenderDetails(); }; const uiToRender = buildUiToRender(ui, handleRender, options?.wrapper); renderResult = render(uiToRender); captureRenderDetails(); if (scenario) { await scenario(renderResult); } cleanup(); global.gc?.(); await options?.afterEach?.(); runResults.push({ duration, count }); } revertRenderPolyfills(); return { ...processRunResults(runResults, { warmupRuns, removeOutliers }), issues: { initialUpdateCount: initialRenderCount - 1, redundantUpdates: detectRedundantUpdates(renderJsonTrees, initialRenderCount) } }; } export function buildUiToRender(ui, onRender, Wrapper) { const uiWithProfiler = /*#__PURE__*/React.createElement(React.Profiler, { id: "REASSURE_ROOT", onRender: onRender }, ui); return Wrapper ? /*#__PURE__*/React.createElement(Wrapper, null, uiWithProfiler) : uiWithProfiler; } //# sourceMappingURL=measure-renders.js.map