A test in our frankly very complicated React app is taking ~1000 times longer to run than expected, and I'm trying to pin down where/why.
I started by manually calling console.time('name of test') in the test file, and then manually dotting console.timeLog('name of test', 'did a thing') around the application in all the places that were called and seemed likely to cause slow downs.
I noticed a lot of these places were inside React hooks - they aren't slow themselves, but were helping me see how long it took for coffee to get there.
I decided I needed to write a monkey patch in a Jest setupFilesAfterEnv file for logging when React hooks callback are called, and what with (for this example, I'll use useEffect)
const React = require('react');
let timeTestName = null;
let doTimeLog = false;
let prevUseEffect;
beforeAll(() => {
({ timeTestName } = global);
const prevUseEffect = React.useEffect;
React.useEffect = (cb, deps) => {
if(doTimeLog && timeTestName && Array.isArray(deps) && !__filename.includes('node_modules')){
console.timeLog(timeTestName, `Use Effect called with ${JSON.stringify(deps, null, 2)}`): // log useEffect being called with timer
}
prevUseEffect(cb, deps);
}
});
beforeEach(() => {
const { testPath } = expect.getState();
if(testPath.endsWith(`${timeTestName}.test.js`)) {
doTimeLog = true;
console.time(timeTestName); // start timer
} else {
doTimerLog = false;
}
});
afterEach(() => {
doTimerLog = false;
console.log(testToTimeName); // end timer
});
afterAll(() => {
React.useEffect = prevUseEffect;
})
However what I really want as well is the variable names in the dependency list, which I cannot get (at least not without changing non-test code).
One thought I had was using a Jest transformer to make all the arrays into objects so I preserve the variable names as keys; something like:
module.exports = {
process(sourceText) {
return {
code: `convertSquareToCurly(sourceText)`,
};
},
};
module.exports = {
transform: {
'matchDepList':
'<rootDir>/deplistTransformer.js',
},
};
So during tests only my useEffects become:
useEffect(() => foo(a, b, c), { a, b, c})
(I will handle this new object in my monkey patch)
I'm reasonably confident I can make the above work.
Then I in my monkey patch I can call console.log(Object.entries(deps)); and prevUseEffect(cb, Object.values(deps));.
However if I'm concerned that calling Object.values will cause the hook's callback to always be called.
I haven't been able to try the transformer yet, but I don't want to waste time writing it if passing Object.values won't work in place of the untransformed dependency list.
Is there another way to monkey patch and get variable names, or would this work?
[{ foo, bar, baz }]instead of[foo, bar, baz]? That would be a new, unequal object on every render and therefore trigger the effect every time, you might as well have no deps at that point.useEffect()with a-lauseEffectDebugger()and go from there if you really want to see all hooks firing.