Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: callstack/reassure
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 919b01f92e52f7a274fc17fab3b6a62610332e48
Choose a base ref
..
head repository: callstack/reassure
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 5f5aa155dc645166290fc6bc4bebf32b11ecf646
Choose a head ref
Showing with 41 additions and 24 deletions.
  1. +7 −24 packages/measure/src/measure-renders.tsx
  2. +34 −0 packages/measure/src/polyfils.ts
31 changes: 7 additions & 24 deletions packages/measure/src/measure-renders.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { performance as perf } from 'perf_hooks';
import * as React from 'react';
import * as logger from '@callstack/reassure-logger';
import { config } from './config';
import { RunResult, processRunResults } from './measure-helpers';
import { showFlagsOutputIfNeeded, writeTestStats } from './output';
import { applyRenderPolyfills, restoreRenderPolyfills } from './polyfils';
import { ElementJsonTree, detectRedundantUpdates } from './redundant-renders';
import { resolveTestingLibrary, getTestingLibrary } from './testing-library';
import type { MeasureRendersResults } from './types';
import { ElementJsonTree, detectRedundantUpdates } from './redundant-renders';

logger.configure({
verbose: process.env.REASSURE_VERBOSE === 'true' || process.env.REASSURE_VERBOSE === '1',
@@ -60,16 +60,15 @@ async function measureRendersInternal(
const testingLibrary = getTestingLibrary();

showFlagsOutputIfNeeded();
applyRenderPolyfills();

const runResults: RunResult[] = [];
let hasTooLateRender = false;

const renderJsonTrees: ElementJsonTree[] = [];
let initialRenderCount = 0;

installPerformanceNow();

for (let i = 0; i < runs + warmupRuns; i += 1) {
for (let iteration = 0; iteration < runs + warmupRuns; iteration += 1) {
let duration = 0;
let count = 0;
let isFinished = false;
@@ -78,7 +77,7 @@ async function measureRendersInternal(

const captureRenderDetails = () => {
// We capture render details only on the first run
if (i !== 0) {
if (iteration !== 0) {
return;
}

@@ -120,15 +119,15 @@ async function measureRendersInternal(
runResults.push({ duration, count });
}

restorePerformanceNow();

if (hasTooLateRender) {
const testName = expect.getState().currentTestName;
logger.warn(
`test "${testName}" still re-renders after test scenario finished.\n\nPlease update your code to wait for all renders to finish.`
);
}

restoreRenderPolyfills();

return {
...processRunResults(runResults, warmupRuns),
issues: {
@@ -151,19 +150,3 @@ export function buildUiToRender(

return Wrapper ? <Wrapper>{uiWithProfiler}</Wrapper> : uiWithProfiler;
}

let originalNow: () => number;

//https://github.com/facebook/react/blob/65a56d0e99261481c721334a3ec4561d173594cd/packages/react-devtools-shared/src/backend/fiber/renderer.js#L294
function installPerformanceNow() {
originalNow = globalThis.performance.now;
globalThis.performance.now = () => perf.now();
}

function restorePerformanceNow() {
if (originalNow == null) {
throw new Error('Called "restorePerformanceNow" without calling "installPerformanceNow" first');
}

globalThis.performance.now = originalNow;
}
34 changes: 34 additions & 0 deletions packages/measure/src/polyfils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { performance as perf } from 'perf_hooks';
import { getTestingLibrary } from './testing-library';

export function applyRenderPolyfills() {
const testingLibrary = getTestingLibrary();
if (testingLibrary === 'react-native') {
polyfillPerformanceNow();
}
}

export function restoreRenderPolyfills() {
const testingLibrary = getTestingLibrary();
if (testingLibrary === 'react-native') {
restorePerformanceNow();
}
}

/**
* React Native Jest preset mocks the global.performance object, with `now()` method being `Date.now()`.
* Ref: https://github.com/facebook/react-native/blob/3dfe22bd27429a43b4648c597b71f7965f31ca65/packages/react-native/jest/setup.js#L41
*
* Then React uses `performance.now()` in `Scheduler` to measure component render time.
* https://github.com/facebook/react/blob/45804af18d589fd2c181f3b020f07661c46b73ea/packages/scheduler/src/forks/Scheduler.js#L59
*/
let originalPerformanceNow: () => number;

function polyfillPerformanceNow() {
originalPerformanceNow = global.performance?.now;
global.performance.now = () => perf.now();
}

function restorePerformanceNow() {
globalThis.performance.now = originalPerformanceNow;
}