From d89f79d6e6ce7b8eaada9d8596e594250651cb2f Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 16 Apr 2019 00:14:49 +0100 Subject: [PATCH] Add Suspense stress test --- .../__snapshots__/store-test.js.snap | 115 ++++++ src/__tests__/setupTests.js | 1 + src/__tests__/store-test.js | 340 ++++++++++++++++++ 3 files changed, 456 insertions(+) diff --git a/src/__tests__/__snapshots__/store-test.js.snap b/src/__tests__/__snapshots__/store-test.js.snap index cd0df483e4..cf299a8c5e 100644 --- a/src/__tests__/__snapshots__/store-test.js.snap +++ b/src/__tests__/__snapshots__/store-test.js.snap @@ -25,6 +25,121 @@ exports[`Store should filter DOM nodes from the store tree: 1: mount 1`] = ` `; +exports[`Store should handle a stress test for Suspense 1`] = ` +[root] + ▾ + + ▾ + + +`; + +exports[`Store should handle a stress test for Suspense 2`] = ` +[root] + ▾ + + ▾ + + +`; + +exports[`Store should handle a stress test for Suspense 3`] = ` +[root] + ▾ + + ▾ + + + + +`; + +exports[`Store should handle a stress test for Suspense 4`] = ` +[root] + ▾ + + ▾ + + + + +`; + +exports[`Store should handle a stress test for Suspense 5`] = ` +[root] + ▾ + + ▾ + + + +`; + +exports[`Store should handle a stress test for Suspense 6`] = ` +[root] + ▾ + + ▾ + + + +`; + +exports[`Store should handle a stress test for Suspense 7`] = ` +[root] + ▾ + + ▾ + + + +`; + +exports[`Store should handle a stress test for Suspense 8`] = ` +[root] + ▾ + + ▾ + + + +`; + +exports[`Store should handle a stress test for Suspense 9`] = ` +[root] + ▾ + + ▾ + + +`; + +exports[`Store should handle a stress test for Suspense 10`] = ` +[root] + ▾ + + + +`; + +exports[`Store should handle a stress test for Suspense 11`] = ` +[root] + ▾ + + ▾ + + +`; + +exports[`Store should handle a stress test for Suspense 12`] = ` +[root] + ▾ + + ▾ + + +`; + exports[`Store should handle a stress test with different tree operations: 1: abcde 1`] = ` [root] ▾ diff --git a/src/__tests__/setupTests.js b/src/__tests__/setupTests.js index c7cdaa0b8e..fefe83b258 100644 --- a/src/__tests__/setupTests.js +++ b/src/__tests__/setupTests.js @@ -33,6 +33,7 @@ env.beforeEach(() => { initBackend(global.__REACT_DEVTOOLS_GLOBAL_HOOK__, agent, global); + global.bridge = bridge; global.store = new Store(bridge); }); env.afterEach(() => { diff --git a/src/__tests__/store-test.js b/src/__tests__/store-test.js index 3480b06622..9c64c93adb 100644 --- a/src/__tests__/store-test.js +++ b/src/__tests__/store-test.js @@ -4,6 +4,7 @@ describe('Store', () => { let React; let ReactDOM; let TestUtils; + // let bridge; let store; let print; @@ -15,6 +16,7 @@ describe('Store', () => { }; beforeEach(() => { + // bridge = global.bridge; store = global.store; React = require('react'); @@ -336,4 +338,342 @@ describe('Store', () => { act(() => root.unmount()); expect(print(store)).toBe(''); }); + + it('should handle a stress test for Suspense', async () => { + const A = () => 'a'; + const B = () => 'b'; + const C = () => 'c'; + const X = () => 'x'; + const Y = () => 'y'; + const Z = () => 'z'; + const a = ; + const b = ; + const c = ; + // const x = ; + // const y = ; + const z = ; + + // prettier-ignore + const steps = [ + a, + [a], + [a, b, c], + [c, b, a], + [c, null, a], + {c}{a}, +
{c}{a}
, +
{a}{b}
, + [[a]], + null, + b, + a + ]; + + const Never = () => { + throw new Promise(() => {}); + }; + + const Root = ({ children }) => { + return children; + }; + + // 1. For each step, check Suspense can render them as initial primary content. + // This is the only step where we use Jest snapshots. + let snapshots = []; + let container = document.createElement('div'); + for (let i = 0; i < steps.length; i++) { + act(() => + ReactDOM.render( + + + {steps[i]} + + , + container + ) + ); + // We snapshot each step once so it doesn't regress. + expect(store).toMatchSnapshot(); + snapshots.push(print(store)); + act(() => ReactDOM.unmountComponentAtNode(container)); + expect(print(store)).toBe(''); + } + + // 2. Verify check Suspense can render same steps as initial fallback content. + for (let i = 0; i < steps.length; i++) { + act(() => + ReactDOM.render( + + + + + + + + + , + container + ) + ); + expect(print(store)).toEqual(snapshots[i]); + act(() => ReactDOM.unmountComponentAtNode(container)); + expect(print(store)).toBe(''); + } + + // 3. Verify we can update from each step to each step in primary mode. + for (let i = 0; i < steps.length; i++) { + for (let j = 0; j < steps.length; j++) { + // Always start with a fresh container and steps[i]. + container = document.createElement('div'); + act(() => + ReactDOM.render( + + + {steps[i]} + + , + container + ) + ); + expect(print(store)).toEqual(snapshots[i]); + // Re-render with steps[j]. + act(() => + ReactDOM.render( + + + {steps[j]} + + , + container + ) + ); + // Verify the successful transition to steps[j]. + expect(print(store)).toEqual(snapshots[j]); + // Clean up after every iteration. + act(() => ReactDOM.unmountComponentAtNode(container)); + expect(print(store)).toBe(''); + } + } + + // TODO: fix the bugs + // // 4. Verify we can update from each step to each step in fallback mode. + // for (let i = 0; i < steps.length; i++) { + // for (let j = 0; j < steps.length; j++) { + // // Always start with a fresh container and steps[i]. + // container = document.createElement('div'); + // act(() => ReactDOM.render( + // + // + // + // + // + // + // + // + // , + // container + // )); + // expect(print(store)).toEqual(snapshots[i]); + // // Re-render with steps[j]. + // act(() => ReactDOM.render( + // + // + // + // + // + // + // + // + // , + // container + // )); + // // Verify the successful transition to steps[j]. + // expect(print(store)).toEqual(snapshots[j]); + // // Clean up after every iteration. + // act(() => ReactDOM.unmountComponentAtNode(container)); + // expect(print(store)).toBe(''); + // } + // } + + // 5. Verify we can update from each step to each step when moving primary -> fallback. + for (let i = 0; i < steps.length; i++) { + for (let j = 0; j < steps.length; j++) { + // Always start with a fresh container and steps[i]. + container = document.createElement('div'); + act(() => + ReactDOM.render( + + + {steps[i]} + + , + container + ) + ); + expect(print(store)).toEqual(snapshots[i]); + // Re-render with steps[j]. + act(() => + ReactDOM.render( + + + + + + + + + , + container + ) + ); + // Verify the successful transition to steps[j]. + expect(print(store)).toEqual(snapshots[j]); + // Clean up after every iteration. + act(() => ReactDOM.unmountComponentAtNode(container)); + expect(print(store)).toBe(''); + } + } + + // TODO: fix the bugs + // 6. Verify we can update from each step to each step when moving fallback -> primary. + // for (let i = 0; i < steps.length; i++) { + // for (let j = 0; j < steps.length; j++) { + // // Always start with a fresh container and steps[i]. + // container = document.createElement('div'); + // act(() => ReactDOM.render( + // + // + // + // + // + // + // + // + // , + // container + // )); + // expect(print(store)).toEqual(snapshots[i]); + // // Re-render with steps[j]. + // act(() => ReactDOM.render( + // + // + // + // {steps[j]} + // + // + // , + // container + // )); + // // Verify the successful transition to steps[j]. + // expect(print(store)).toEqual(snapshots[j]); + // // Clean up after every iteration. + // act(() => ReactDOM.unmountComponentAtNode(container)); + // expect(print(store)).toBe(''); + // } + // } + + // TODO: fix the bugs + // 7. Verify we can update from each step to each step when toggling Suspense. + // for (let i = 0; i < steps.length; i++) { + // for (let j = 0; j < steps.length; j++) { + // // Always start with a fresh container and steps[i]. + // container = document.createElement('div'); + // act(() => ReactDOM.render( + // + // + // + // {steps[i]} + // + // + // , + // container + // )); + + // // We get ID from the index in the tree above: + // // Root, X, Suspense, ... + // // ^ (index is 2) + // const suspenseID = store.getElementIDAtIndex(2); + + // // Force fallback. + // expect(print(store)).toEqual(snapshots[i]); + // act(() => { + // const suspenseID = store.getElementIDAtIndex(2); + // bridge.send('overrideSuspense', { + // id: suspenseID, + // rendererID: store.getRendererIDForElement(suspenseID), + // forceFallback: true + // }); + // }) + // expect(print(store)).toEqual(snapshots[j]); + + // // Stop forcing fallback. + // act(() => { + // bridge.send('overrideSuspense', { + // id: suspenseID, + // rendererID: store.getRendererIDForElement(suspenseID), + // forceFallback: false + // }); + // }) + // expect(print(store)).toEqual(snapshots[i]); + + // // Trigger actual fallback. + // act(() => ReactDOM.render( + // + // + // + // + // + // + // + // + // , + // container + // )); + // expect(print(store)).toEqual(snapshots[j]); + + // // Force fallback while we're in fallback mode. + // act(() => { + // bridge.send('overrideSuspense', { + // id: suspenseID, + // rendererID: store.getRendererIDForElement(suspenseID), + // forceFallback: true + // }); + // }) + // // Keep seeing fallback content. + // expect(print(store)).toEqual(snapshots[j]); + + // // Switch to primary mode. + // act(() => ReactDOM.render( + // + // + // + // {steps[i]} + // + // + // , + // container + // )); + // // Fallback is still forced though. + // expect(print(store)).toEqual(snapshots[j]); + + // // Stop forcing fallback. This reverts to primary content. + // act(() => { + // bridge.send('overrideSuspense', { + // id: suspenseID, + // rendererID: store.getRendererIDForElement(suspenseID), + // forceFallback: false + // }); + // }) + // // Now we see primary content. + // expect(print(store)).toEqual(snapshots[i]); + + // // Clean up after every iteration. + // act(() => ReactDOM.unmountComponentAtNode(container)); + // expect(print(store)).toBe(''); + // } + // } + + // TODO: + // Test Concurrent Mode + }); });