@samitouri / QOS-React-2 / commits / 435259f189

Add better test coverage for hooks in DevTools Profiler's change-detection (#17816)

Brian Vaughn committed Jan 9, 2020 at 13:15 UTC 435259f189526fa32b8c9cdab7d7b2c1dc260bc8
2 files changed +555
packages/react-devtools-shared/src/__tests__/__snapshots__/profilingCache-test.js.snap
+431
@@ -2121,6 +2121,437 @@ Object {
2121 }
2122 `;
2123
2124 +exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 0 1`] = `
2125 +Object {
2126 + "changeDescriptions": Map {
2127 + 3 => Object {
2128 + "context": null,
2129 + "didHooksChange": false,
2130 + "isFirstMount": true,
2131 + "props": null,
2132 + "state": null,
2133 + },
2134 + },
2135 + "duration": 0,
2136 + "fiberActualDurations": Map {
2137 + 1 => 0,
2138 + 2 => 0,
2139 + 3 => 0,
2140 + },
2141 + "fiberSelfDurations": Map {
2142 + 1 => 0,
2143 + 2 => 0,
2144 + 3 => 0,
2145 + },
2146 + "interactionIDs": Array [],
2147 + "priorityLevel": "Immediate",
2148 + "timestamp": 0,
2149 +}
2150 +`;
2151 +
2152 +exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 1 1`] = `
2153 +Object {
2154 + "changeDescriptions": Map {
2155 + 3 => Object {
2156 + "context": null,
2157 + "didHooksChange": false,
2158 + "isFirstMount": false,
2159 + "props": Array [
2160 + "count",
2161 + ],
2162 + "state": null,
2163 + },
2164 + },
2165 + "duration": 0,
2166 + "fiberActualDurations": Map {
2167 + 3 => 0,
2168 + 2 => 0,
2169 + 1 => 0,
2170 + },
2171 + "fiberSelfDurations": Map {
2172 + 3 => 0,
2173 + 2 => 0,
2174 + 1 => 0,
2175 + },
2176 + "interactionIDs": Array [],
2177 + "priorityLevel": "Immediate",
2178 + "timestamp": 0,
2179 +}
2180 +`;
2181 +
2182 +exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 2 1`] = `
2183 +Object {
2184 + "changeDescriptions": Map {
2185 + 3 => Object {
2186 + "context": null,
2187 + "didHooksChange": true,
2188 + "isFirstMount": false,
2189 + "props": Array [],
2190 + "state": null,
2191 + },
2192 + },
2193 + "duration": 0,
2194 + "fiberActualDurations": Map {
2195 + 3 => 0,
2196 + },
2197 + "fiberSelfDurations": Map {
2198 + 3 => 0,
2199 + },
2200 + "interactionIDs": Array [],
2201 + "priorityLevel": "Immediate",
2202 + "timestamp": 0,
2203 +}
2204 +`;
2205 +
2206 +exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 3 1`] = `
2207 +Object {
2208 + "changeDescriptions": Map {
2209 + 3 => Object {
2210 + "context": null,
2211 + "didHooksChange": true,
2212 + "isFirstMount": false,
2213 + "props": Array [],
2214 + "state": null,
2215 + },
2216 + },
2217 + "duration": 0,
2218 + "fiberActualDurations": Map {
2219 + 3 => 0,
2220 + },
2221 + "fiberSelfDurations": Map {
2222 + 3 => 0,
2223 + },
2224 + "interactionIDs": Array [],
2225 + "priorityLevel": "Immediate",
2226 + "timestamp": 0,
2227 +}
2228 +`;
2229 +
2230 +exports[`ProfilingCache should properly detect changed hooks: CommitDetails commitIndex: 4 1`] = `
2231 +Object {
2232 + "changeDescriptions": Map {
2233 + 3 => Object {
2234 + "context": null,
2235 + "didHooksChange": false,
2236 + "isFirstMount": false,
2237 + "props": Array [],
2238 + "state": null,
2239 + },
2240 + },
2241 + "duration": 0,
2242 + "fiberActualDurations": Map {
2243 + 3 => 0,
2244 + 2 => 0,
2245 + 1 => 0,
2246 + },
2247 + "fiberSelfDurations": Map {
2248 + 3 => 0,
2249 + 2 => 0,
2250 + 1 => 0,
2251 + },
2252 + "interactionIDs": Array [],
2253 + "priorityLevel": "Immediate",
2254 + "timestamp": 0,
2255 +}
2256 +`;
2257 +
2258 +exports[`ProfilingCache should properly detect changed hooks: imported data 1`] = `
2259 +Object {
2260 + "dataForRoots": Array [
2261 + Object {
2262 + "commitData": Array [
2263 + Object {
2264 + "changeDescriptions": Array [
2265 + Array [
2266 + 3,
2267 + Object {
2268 + "context": null,
2269 + "didHooksChange": false,
2270 + "isFirstMount": true,
2271 + "props": null,
2272 + "state": null,
2273 + },
2274 + ],
2275 + ],
2276 + "duration": 0,
2277 + "fiberActualDurations": Array [
2278 + Array [
2279 + 1,
2280 + 0,
2281 + ],
2282 + Array [
2283 + 2,
2284 + 0,
2285 + ],
2286 + Array [
2287 + 3,
2288 + 0,
2289 + ],
2290 + ],
2291 + "fiberSelfDurations": Array [
2292 + Array [
2293 + 1,
2294 + 0,
2295 + ],
2296 + Array [
2297 + 2,
2298 + 0,
2299 + ],
2300 + Array [
2301 + 3,
2302 + 0,
2303 + ],
2304 + ],
2305 + "interactionIDs": Array [],
2306 + "priorityLevel": "Immediate",
2307 + "timestamp": 0,
2308 + },
2309 + Object {
2310 + "changeDescriptions": Array [
2311 + Array [
2312 + 3,
2313 + Object {
2314 + "context": null,
2315 + "didHooksChange": false,
2316 + "isFirstMount": false,
2317 + "props": Array [
2318 + "count",
2319 + ],
2320 + "state": null,
2321 + },
2322 + ],
2323 + ],
2324 + "duration": 0,
2325 + "fiberActualDurations": Array [
2326 + Array [
2327 + 3,
2328 + 0,
2329 + ],
2330 + Array [
2331 + 2,
2332 + 0,
2333 + ],
2334 + Array [
2335 + 1,
2336 + 0,
2337 + ],
2338 + ],
2339 + "fiberSelfDurations": Array [
2340 + Array [
2341 + 3,
2342 + 0,
2343 + ],
2344 + Array [
2345 + 2,
2346 + 0,
2347 + ],
2348 + Array [
2349 + 1,
2350 + 0,
2351 + ],
2352 + ],
2353 + "interactionIDs": Array [],
2354 + "priorityLevel": "Immediate",
2355 + "timestamp": 0,
2356 + },
2357 + Object {
2358 + "changeDescriptions": Array [
2359 + Array [
2360 + 3,
2361 + Object {
2362 + "context": null,
2363 + "didHooksChange": true,
2364 + "isFirstMount": false,
2365 + "props": Array [],
2366 + "state": null,
2367 + },
2368 + ],
2369 + ],
2370 + "duration": 0,
2371 + "fiberActualDurations": Array [
2372 + Array [
2373 + 3,
2374 + 0,
2375 + ],
2376 + ],
2377 + "fiberSelfDurations": Array [
2378 + Array [
2379 + 3,
2380 + 0,
2381 + ],
2382 + ],
2383 + "interactionIDs": Array [],
2384 + "priorityLevel": "Immediate",
2385 + "timestamp": 0,
2386 + },
2387 + Object {
2388 + "changeDescriptions": Array [
2389 + Array [
2390 + 3,
2391 + Object {
2392 + "context": null,
2393 + "didHooksChange": true,
2394 + "isFirstMount": false,
2395 + "props": Array [],
2396 + "state": null,
2397 + },
2398 + ],
2399 + ],
2400 + "duration": 0,
2401 + "fiberActualDurations": Array [
2402 + Array [
2403 + 3,
2404 + 0,
2405 + ],
2406 + ],
2407 + "fiberSelfDurations": Array [
2408 + Array [
2409 + 3,
2410 + 0,
2411 + ],
2412 + ],
2413 + "interactionIDs": Array [],
2414 + "priorityLevel": "Immediate",
2415 + "timestamp": 0,
2416 + },
2417 + Object {
2418 + "changeDescriptions": Array [
2419 + Array [
2420 + 3,
2421 + Object {
2422 + "context": null,
2423 + "didHooksChange": false,
2424 + "isFirstMount": false,
2425 + "props": Array [],
2426 + "state": null,
2427 + },
2428 + ],
2429 + ],
2430 + "duration": 0,
2431 + "fiberActualDurations": Array [
2432 + Array [
2433 + 3,
2434 + 0,
2435 + ],
2436 + Array [
2437 + 2,
2438 + 0,
2439 + ],
2440 + Array [
2441 + 1,
2442 + 0,
2443 + ],
2444 + ],
2445 + "fiberSelfDurations": Array [
2446 + Array [
2447 + 3,
2448 + 0,
2449 + ],
2450 + Array [
2451 + 2,
2452 + 0,
2453 + ],
2454 + Array [
2455 + 1,
2456 + 0,
2457 + ],
2458 + ],
2459 + "interactionIDs": Array [],
2460 + "priorityLevel": "Immediate",
2461 + "timestamp": 0,
2462 + },
2463 + ],
2464 + "displayName": "Component",
2465 + "initialTreeBaseDurations": Array [],
2466 + "interactionCommits": Array [],
2467 + "interactions": Array [],
2468 + "operations": Array [
2469 + Array [
2470 + 1,
2471 + 1,
2472 + 27,
2473 + 16,
2474 + 67,
2475 + 111,
2476 + 110,
2477 + 116,
2478 + 101,
2479 + 120,
2480 + 116,
2481 + 46,
2482 + 80,
2483 + 114,
2484 + 111,
2485 + 118,
2486 + 105,
2487 + 100,
2488 + 101,
2489 + 114,
2490 + 9,
2491 + 67,
2492 + 111,
2493 + 109,
2494 + 112,
2495 + 111,
2496 + 110,
2497 + 101,
2498 + 110,
2499 + 116,
2500 + 1,
2501 + 1,
2502 + 11,
2503 + 1,
2504 + 1,
2505 + 1,
2506 + 2,
2507 + 2,
2508 + 1,
2509 + 0,
2510 + 1,
2511 + 0,
2512 + 4,
2513 + 2,
2514 + 0,
2515 + 1,
2516 + 3,
2517 + 5,
2518 + 2,
2519 + 0,
2520 + 2,
2521 + 0,
2522 + 4,
2523 + 3,
2524 + 0,
2525 + ],
2526 + Array [
2527 + 1,
2528 + 1,
2529 + 0,
2530 + ],
2531 + Array [
2532 + 1,
2533 + 1,
2534 + 0,
2535 + ],
2536 + Array [
2537 + 1,
2538 + 1,
2539 + 0,
2540 + ],
2541 + Array [
2542 + 1,
2543 + 1,
2544 + 0,
2545 + ],
2546 + ],
2547 + "rootID": 1,
2548 + "snapshots": Array [],
2549 + },
2550 + ],
2551 + "version": 4,
2552 +}
2553 +`;
2554 +
2555 exports[`ProfilingCache should record changed props/state/context/hooks: CommitDetails commitIndex: 0 1`] = `
2556 Object {
2557 "changeDescriptions": Map {
packages/react-devtools-shared/src/__tests__/profilingCache-test.js
+124
@@ -308,6 +308,130 @@ describe('ProfilingCache', () => {
308 }
309 });
310
311 + it('should properly detect changed hooks', () => {
312 + const Context = React.createContext(0);
313 +
314 + function reducer(state, action) {
315 + switch (action.type) {
316 + case 'invert':
317 + return {value: !state.value};
318 + default:
319 + throw new Error();
320 + }
321 + }
322 +
323 + let dispatch = null;
324 + let setState = null;
325 +
326 + const Component = ({count, string}) => {
327 + // These hooks may change and initiate re-renders.
328 + setState = React.useState('abc')[1];
329 + dispatch = React.useReducer(reducer, {value: true})[1];
330 +
331 + // This hook's return value may change between renders,
332 + // but the hook itself isn't stateful.
333 + React.useContext(Context);
334 +
335 + // These hooks and their dependencies may not change between renders.
336 + // We're using them to ensure that they don't trigger false positives.
337 + React.useCallback(() => () => {}, [string]);
338 + React.useMemo(() => string, [string]);
339 +
340 + // These hooks never "change".
341 + React.useEffect(() => {}, [string]);
342 + React.useLayoutEffect(() => {}, [string]);
343 +
344 + return null;
345 + };
346 +
347 + const container = document.createElement('div');
348 +
349 + utils.act(() => store.profilerStore.startProfiling());
350 + utils.act(() =>
351 + ReactDOM.render(
352 + <Context.Provider value={true}>
353 + <Component count={1} />
354 + </Context.Provider>,
355 + container,
356 + ),
357 + );
358 +
359 + // Second render has no changed hooks, only changed props.
360 + utils.act(() =>
361 + ReactDOM.render(
362 + <Context.Provider value={true}>
363 + <Component count={2} />
364 + </Context.Provider>,
365 + container,
366 + ),
367 + );
368 +
369 + // Third render has a changed reducer hook
370 + utils.act(() => dispatch({type: 'invert'}));
371 +
372 + // Fourth render has a changed state hook
373 + utils.act(() => setState('def'));
374 +
375 + // Fifth render has a changed context value, but no changed hook.
376 + // Technically, DevTools will miss this "context" change since it only tracks legacy context.
377 + utils.act(() =>
378 + ReactDOM.render(
379 + <Context.Provider value={false}>
380 + <Component count={2} />
381 + </Context.Provider>,
382 + container,
383 + ),
384 + );
385 +
386 + utils.act(() => store.profilerStore.stopProfiling());
387 +
388 + const allCommitData = [];
389 +
390 + function Validator({commitIndex, previousCommitDetails, rootID}) {
391 + const commitData = store.profilerStore.getCommitData(rootID, commitIndex);
392 + if (previousCommitDetails != null) {
393 + expect(commitData).toEqual(previousCommitDetails);
394 + } else {
395 + allCommitData.push(commitData);
396 + expect(commitData).toMatchSnapshot(
397 + `CommitDetails commitIndex: ${commitIndex}`,
398 + );
399 + }
400 + return null;
401 + }
402 +
403 + const rootID = store.roots[0];
404 +
405 + for (let commitIndex = 0; commitIndex < 5; commitIndex++) {
406 + utils.act(() => {
407 + TestRenderer.create(
408 + <Validator
409 + commitIndex={commitIndex}
410 + previousCommitDetails={null}
411 + rootID={rootID}
412 + />,
413 + );
414 + });
415 + }
416 +
417 + expect(allCommitData).toHaveLength(5);
418 +
419 + // Export and re-import profile data and make sure it is retained.
420 + utils.exportImportHelper(bridge, store);
421 +
422 + for (let commitIndex = 0; commitIndex < 5; commitIndex++) {
423 + utils.act(() => {
424 + TestRenderer.create(
425 + <Validator
426 + commitIndex={commitIndex}
427 + previousCommitDetails={allCommitData[commitIndex]}
428 + rootID={rootID}
429 + />,
430 + );
431 + });
432 + }
433 + });
434 +
435 it('should calculate a self duration based on actual children (not filtered children)', () => {
436 store.componentFilters = [utils.createDisplayNameFilter('^Parent$')];
437