diff --git a/package.json b/package.json index 0c42947e5c..0dfb1b3c71 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "/src/__tests__/setupTests" ], "snapshotSerializers": [ - "/src/__tests__/profilingSummarySerializer", "/src/__tests__/storeSerializer" ], "testMatch": [ diff --git a/src/__tests__/__snapshots__/profiling-test.js.snap b/src/__tests__/__snapshots__/profiling-test.js.snap index a1e084986e..4f9c7ae56d 100644 --- a/src/__tests__/__snapshots__/profiling-test.js.snap +++ b/src/__tests__/__snapshots__/profiling-test.js.snap @@ -1,5 +1,74 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`profiling commitDetails should be collected for each commit: 1: mount 1`] = ` +[root] + ▾ + + +`; + +exports[`profiling commitDetails should be collected for each commit: 2: add child 1`] = ` +[root] + ▾ + + + +`; + +exports[`profiling commitDetails should be collected for each commit: 3: remove children 1`] = ` +[root] + ▾ + +`; + +exports[`profiling commitDetails should be collected for each commit: 4: profiling stopped 1`] = ` +[root] + ▾ + +`; + +exports[`profiling commitDetails should be collected for each commit: 5: CommitDetails: mount 1`] = ` +Object { + "actualDurations": Map { + 1 => 14, + 2 => 14, + 3 => 2, + 4 => 2, + }, + "commitIndex": 0, + "interactions": Array [], + "rootID": 1, +} +`; + +exports[`profiling commitDetails should be collected for each commit: 6: CommitDetails: add child 1`] = ` +Object { + "actualDurations": Map { + 3 => 2, + 4 => 2, + 5 => 2, + 2 => 16, + 1 => 16, + }, + "commitIndex": 1, + "interactions": Array [], + "rootID": 1, +} +`; + +exports[`profiling commitDetails should be collected for each commit: 7: CommitDetails: remove children 1`] = ` +Object { + "actualDurations": Map { + 3 => 2, + 2 => 12, + 1 => 12, + }, + "commitIndex": 2, + "interactions": Array [], + "rootID": 1, +} +`; + exports[`profiling profilingSummary should be collected for each commit: 1: mount 1`] = ` [root] ▾ @@ -28,34 +97,22 @@ exports[`profiling profilingSummary should be collected for each commit: 4: prof `; exports[`profiling profilingSummary should be collected for each commit: ProfilingSummary 1`] = ` -{ +Object { + "commitDurations": Array [ + 16, + 12, + ], + "commitTimes": Array [ + 16, + 28, + ], + "initialTreeBaseDurations": Map { + 1 => 14, + 2 => 14, + 3 => 2, + 4 => 2, + }, + "interactionCount": 0, "rootID": 1, - "commitDurations": [ - 16, - 12 - ], - "commitTimes": [ - 16, - 28 - ], - "initialTreeBaseDurations": [ - [ - 1, - 14 - ], - [ - 2, - 14 - ], - [ - 3, - 2 - ], - [ - 4, - 2 - ] - ], - "interactionCount": 0 } `; diff --git a/src/__tests__/profiling-test.js b/src/__tests__/profiling-test.js index e740d45e4a..253270cf13 100644 --- a/src/__tests__/profiling-test.js +++ b/src/__tests__/profiling-test.js @@ -76,6 +76,93 @@ describe('profiling', () => { }); }); + describe('commitDetails', () => { + it('should be collected for each commit', async done => { + const Parent = ({ count }) => { + Scheduler.advanceTime(10); + return new Array(count) + .fill(true) + .map((_, index) => ); + }; + const Child = () => { + Scheduler.advanceTime(2); + return null; + }; + + const container = document.createElement('div'); + + utils.act(() => store.startProfiling()); + + utils.act(() => ReactDOM.render(, container)); + expect(store).toMatchSnapshot('1: mount'); + + utils.act(() => ReactDOM.render(, container)); + expect(store).toMatchSnapshot('2: add child'); + + utils.act(() => ReactDOM.render(, container)); + expect(store).toMatchSnapshot('3: remove children'); + + utils.act(() => store.stopProfiling()); + expect(store).toMatchSnapshot('4: profiling stopped'); + + let commitDetails; + function Suspender({ commitIndex, rendererID, rootID }) { + commitDetails = store.profilingCache.CommitDetails.read({ + commitIndex, + rendererID, + rootID, + }); + return null; + } + + const rendererID = utils.getRendererID(); + const rootID = store.roots[0]; + + await utils.actSuspense(() => + TestRenderer.create( + + + + ) + ); + expect(commitDetails).toMatchSnapshot('5: CommitDetails: mount'); + + await utils.actSuspense(() => + TestRenderer.create( + + + + ) + ); + expect(commitDetails).toMatchSnapshot('6: CommitDetails: add child'); + + await utils.actSuspense(() => + TestRenderer.create( + + + + ) + ); + expect(commitDetails).toMatchSnapshot( + '7: CommitDetails: remove children' + ); + + done(); + }); + }); + it('should remove profiling data when roots are unmounted', async () => { const Parent = ({ count }) => new Array(count).fill(true).map((_, index) => ); diff --git a/src/__tests__/profilingSummarySerializer.js b/src/__tests__/profilingSummarySerializer.js deleted file mode 100644 index a2bcb1b5a0..0000000000 --- a/src/__tests__/profilingSummarySerializer.js +++ /dev/null @@ -1,25 +0,0 @@ -// test() is part of Jest's serializer API -export function test(maybeProfilingSummary) { - return ( - typeof maybeProfilingSummary === 'object' && - maybeProfilingSummary !== null && - typeof maybeProfilingSummary.rootID === 'number' && - Array.isArray(maybeProfilingSummary.commitDurations) && - Array.isArray(maybeProfilingSummary.commitTimes) && - typeof maybeProfilingSummary.initialTreeBaseDurations === 'object' && - maybeProfilingSummary.initialTreeBaseDurations !== null && - typeof maybeProfilingSummary.interactionCount === 'number' - ); -} - -// print() is part of Jest's serializer API -export function print(profilingSummary, serialize, indent) { - return JSON.stringify( - { - ...profilingSummary, - initialTreeBaseDurations: [...profilingSummary.initialTreeBaseDurations], - }, - null, - 2 - ); -} diff --git a/src/__tests__/setupTests.js b/src/__tests__/setupTests.js index b498a014fd..2aec331a95 100644 --- a/src/__tests__/setupTests.js +++ b/src/__tests__/setupTests.js @@ -1,13 +1,17 @@ // @flow -import Agent from 'src/backend/agent'; -import { initBackend } from 'src/backend'; -import Bridge from 'src/bridge'; -import Store from 'src/devtools/store'; -import { installHook } from 'src/hook'; - const env = jasmine.getEnv(); env.beforeEach(() => { + // These files should be required (and re-reuired) before each test, + // rather than imported at the head of the module. + // That's because we reset modules between tests, + // which disconnects the DevTool's cache from the current dispatcher ref. + const Agent = require('src/backend/agent').default; + const { initBackend } = require('src/backend'); + const Bridge = require('src/bridge').default; + const Store = require('src/devtools/store').default; + const { installHook } = require('src/hook'); + // Fake timers let us flush Bridge operations between setup and assertions. jest.useFakeTimers(); diff --git a/src/__tests__/storeSerializer.js b/src/__tests__/storeSerializer.js index 80a5b3f1e1..6e6400aaf5 100644 --- a/src/__tests__/storeSerializer.js +++ b/src/__tests__/storeSerializer.js @@ -1,8 +1,9 @@ -import Store from 'src/devtools/store'; - // test() is part of Jest's serializer API export function test(maybeStore) { - return maybeStore instanceof Store; + // It's important to lazy-require the Store rather than imported at the head of the module. + // Because we reset modules between tests, different Store implementations will be used for each test. + // Unfortunately Jest does not reset its own serializer modules. + return maybeStore instanceof require('src/devtools/store').default; } // print() is part of Jest's serializer API diff --git a/src/devtools/cache.js b/src/devtools/cache.js index 1e12798030..7116600b60 100644 --- a/src/devtools/cache.js +++ b/src/devtools/cache.js @@ -49,6 +49,8 @@ const Pending = 0; const Resolved = 1; const Rejected = 2; +// TODO This file isn't being re-imported between tests it seems, so it's getting disconnected + const ReactCurrentDispatcher = (React: any) .__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher;