From 905b36dcfb01d397f03caa3a0e662df26afc144a Mon Sep 17 00:00:00 2001 From: vzaidman Date: Fri, 12 Apr 2019 13:50:48 +0300 Subject: [PATCH] #18 - add react-redux test + demo, notify about a re-rendered element in props and ignore if it's props are different, improve memoized tracking performance, fix hooks notifications of memoized components were tracked as of inner WDYR component, fix memoized components notifications --- demo/nollup.config.js | 4 +- demo/src/index.js | 4 +- demo/src/reactRedux/index.js | 62 +++++++++ package.json | 6 +- src/calculateDeepEqualDiffs.js | 15 ++- src/calculateDeepEqualDiffs.test.js | 96 ++++++++++++++ src/defaultNotifier.js | 2 +- src/{hooks.test.js => index.hooks.test.js} | 0 src/index.reactRedux.test.js | 140 +++++++++++++++++++++ src/whyDidYouRender.js | 21 ++-- yarn.lock | 133 +++++++++++++++++++- 11 files changed, 461 insertions(+), 22 deletions(-) create mode 100644 demo/src/reactRedux/index.js rename src/{hooks.test.js => index.hooks.test.js} (100%) create mode 100644 src/index.reactRedux.test.js diff --git a/demo/nollup.config.js b/demo/nollup.config.js index 246cd52..4c32ec4 100644 --- a/demo/nollup.config.js +++ b/demo/nollup.config.js @@ -19,6 +19,8 @@ module.exports = { exclude: 'node_modules/**' }), node_resolve(), - commonjs() + commonjs({ + exclude: ['node_modules/react-redux/**'] + }) ] } diff --git a/demo/src/index.js b/demo/src/index.js index 11e0e32..81793dd 100644 --- a/demo/src/index.js +++ b/demo/src/index.js @@ -19,6 +19,7 @@ import useReducer from './hooks/useReducer' import useContext from './hooks/useContext' import useMemo from './hooks/useMemo' import strict from './strict' +import reactRedux from './reactRedux' import whyDidYouRender from './whyDidYouRender' @@ -38,7 +39,8 @@ const demosList = { useReducer, useContext, useMemo, - strict + strict, + reactRedux } const defaultDemoName = 'bigList' diff --git a/demo/src/reactRedux/index.js b/demo/src/reactRedux/index.js new file mode 100644 index 0000000..d1d4d5e --- /dev/null +++ b/demo/src/reactRedux/index.js @@ -0,0 +1,62 @@ +import React from 'react' +import ReactDom from 'react-dom' +import {createStore} from 'redux' +import connect from 'react-redux/lib/connect/connect' +import Provider from 'react-redux/lib/components/Provider' +import _ from 'lodash' + +connect = connect.default +Provider = Provider.default + +export default { + description: 'React Redux', + fn({domElement, whyDidYouRender}){ + whyDidYouRender(React) + + const initialState = {a: {b: 'c'}} + + const rootReducer = (state, action) => { + if(action.type === 'randomObj'){ + return {a: {b: `${Math.random()}`}} + } + + if(action.type === 'deepEqlObj'){ + return _.cloneDeep(state) + } + + return state + } + + const store = createStore(rootReducer, initialState) + + const SimpleComponent = ({a, randomObj, deepEqlObj, sameObj}) => { + return ( +
+ {`{a.b} is: ${a.b}`} + + + +
+ ) + } + + const ConnectedSimpleComponent = connect( + state => ({a: state.a}), + ({ + randomObj: () => ({type: 'randomObj'}), + deepEqlObj: () => ({type: 'deepEqlObj'}), + sameObj: () => ({type: 'sameObj'}) + }) + )(SimpleComponent) + + ConnectedSimpleComponent.whyDidYouRender = true + + const Main = () => ( + + + + ) + + ReactDom.render(
, domElement) + } +} diff --git a/package.json b/package.json index 71fad07..c99dad8 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "start": "cross-env PORT=3003 NODE_ENV=development node demo/serve", "build": "cross-env NODE_ENV=production rollup --config", "test": "cross-env NODE_ENV=development TEST=true jest", - "test:watch": "yarn test --watchAll", + "test:watch": "yarn test --watch", "lint": "esw . --ext=js --cache --cache-location .temp/eslint-cache", "lint:fix": "yarn lint --fix", "lint:watch": "yarn lint --watch", @@ -64,12 +64,16 @@ "husky": "^1.3.1", "jest": "^24.7.1", "jest-cli": "^24.7.1", + "jest-dom": "^3.1.3", "magic-string": "^0.25.2", "nollup": "^0.5.3", "react": "^16.8.6", "react-dom": "^16.8.6", "react-hot-loader": "^4.8.3", + "react-redux": "^7.0.2", "react-test-renderer": "^16.8.6", + "react-testing-library": "^6.1.2", + "redux": "^4.0.1", "rimraf": "^2.6.3", "rollup": "^1.10.0", "rollup-plugin-babel": "^4.3.2", diff --git a/src/calculateDeepEqualDiffs.js b/src/calculateDeepEqualDiffs.js index 91387cc..25c9dd3 100644 --- a/src/calculateDeepEqualDiffs.js +++ b/src/calculateDeepEqualDiffs.js @@ -63,9 +63,18 @@ function accumulateDeepEqualDiffs(a, b, diffsAccumulator, pathString = ''){ } if(isReactElement(a) && isReactElement(b)){ - return a.type === b.type ? - trackDiff(a, b, diffsAccumulator, pathString, diffTypes.reactElement) : - trackDiff(a, b, diffsAccumulator, pathString, diffTypes.different) + if(a.type !== b.type){ + return trackDiff(a, b, diffsAccumulator, pathString, diffTypes.different) + } + + else{ + const reactElementPropsAreDeepEqual = + accumulateDeepEqualDiffs(a.props, b.props, diffsAccumulator, `${pathString}.props`) + + return reactElementPropsAreDeepEqual ? + trackDiff(a, b, diffsAccumulator, pathString, diffTypes.reactElement) : + trackDiff(a, b, diffsAccumulator, pathString, diffTypes.different) + } } if(isFunction(a) && isFunction(b)){ diff --git a/src/calculateDeepEqualDiffs.test.js b/src/calculateDeepEqualDiffs.test.js index 32fc56c..34a454a 100644 --- a/src/calculateDeepEqualDiffs.test.js +++ b/src/calculateDeepEqualDiffs.test.js @@ -228,6 +228,12 @@ describe('calculateDeepEqualDiffs', () => { const diffs = calculateDeepEqualDiffs(prevValue, nextValue) expect(diffs).toEqual([ + { + pathString: '.a.props', + prevValue: {children: 'hi!'}, + nextValue: {children: 'hi!'}, + diffType: diffTypes.deepEquals + }, { pathString: '.a', prevValue: prevValue.a, @@ -259,6 +265,49 @@ describe('calculateDeepEqualDiffs', () => { const diffs = calculateDeepEqualDiffs(prevValue, nextValue) expect(diffs).toEqual([ + { + pathString: '.a.props', + prevValue: {}, + nextValue: {}, + diffType: diffTypes.deepEquals + }, + { + pathString: '.a', + prevValue: prevValue.a, + nextValue: nextValue.a, + diffType: diffTypes.reactElement + }, + { + pathString: '', + prevValue, + nextValue, + diffType: diffTypes.deepEquals + } + ]) + }) + + test('react class pure component instance', () => { + class MyComponent extends React.PureComponent{ + render(){ + return
hi!
+ } + } + + const tooltip = + const tooltip2 = + + const prevValue = {a: tooltip} + const nextValue = {a: tooltip2} + + const diffs = calculateDeepEqualDiffs(prevValue, nextValue) + + expect(diffs).toEqual([ + { + pathString: '.a.props', + prevValue: {}, + nextValue: {}, + diffType: diffTypes.deepEquals + }, { pathString: '.a', prevValue: prevValue.a, @@ -288,6 +337,47 @@ describe('calculateDeepEqualDiffs', () => { const diffs = calculateDeepEqualDiffs(prevValue, nextValue) expect(diffs).toEqual([ + { + pathString: '.a.props', + prevValue: {}, + nextValue: {}, + diffType: diffTypes.deepEquals + }, + { + pathString: '.a', + prevValue: prevValue.a, + nextValue: nextValue.a, + diffType: diffTypes.reactElement + }, + { + pathString: '', + prevValue, + nextValue, + diffType: diffTypes.deepEquals + } + ]) + }) + + test('react memoized functional component instance', () => { + const MyFunctionalComponent = React.memo(() => ( +
hi!
+ )) + + const tooltip = + const tooltip2 = + + const prevValue = {a: tooltip} + const nextValue = {a: tooltip2} + + const diffs = calculateDeepEqualDiffs(prevValue, nextValue) + + expect(diffs).toEqual([ + { + pathString: '.a.props', + prevValue: {a: 1}, + nextValue: {a: 1}, + diffType: diffTypes.deepEquals + }, { pathString: '.a', prevValue: prevValue.a, @@ -363,6 +453,12 @@ describe('calculateDeepEqualDiffs', () => { const diffs = calculateDeepEqualDiffs(prevValue, nextValue) expect(diffs).toEqual([ + { + pathString: '.b[0].tooltip.props', + prevValue: {children: 'hi'}, + nextValue: {children: 'hi'}, + diffType: diffTypes.deepEquals + }, { pathString: '.b[0].tooltip', prevValue: prevValue.b[0].tooltip, diff --git a/src/defaultNotifier.js b/src/defaultNotifier.js index 248da0f..390e176 100644 --- a/src/defaultNotifier.js +++ b/src/defaultNotifier.js @@ -46,7 +46,7 @@ function logDifference({Component, displayName, hookName, prefixMessage, diffObj options.consoleLog({[displayName]: Component}, `${prefixMessage} of ${diffObjType} changes:`) differences.forEach(({pathString, diffType, prevValue, nextValue}) => { options.consoleGroup( - `%c${diffObjType === 'hook' ? `hook ${hookName} ` : `${diffObjType}.`}%c${pathString}%c`, + `%c${diffObjType === 'hook' ? `[hook ${hookName} result]` : `${diffObjType}.`}%c${pathString}%c`, `color:${options.diffNameColor};`, `color:${options.diffPathColor};`, 'color:default;' ) options.consoleLog( diff --git a/src/hooks.test.js b/src/index.hooks.test.js similarity index 100% rename from src/hooks.test.js rename to src/index.hooks.test.js diff --git a/src/index.reactRedux.test.js b/src/index.reactRedux.test.js new file mode 100644 index 0000000..9498375 --- /dev/null +++ b/src/index.reactRedux.test.js @@ -0,0 +1,140 @@ +/* eslint-disable */ + +// import React from 'react' +import React from 'react' +import {createStore} from 'redux' +import {connect, Provider} from 'react-redux' +import {cloneDeep} from 'lodash' +import * as rtl from 'react-testing-library' +import 'jest-dom/extend-expect' + +import whyDidYouRender from './index' + +describe('react-redux', () => { + const initialState = {a: {b: 'c'}} + + const rootReducer = (state, action) => { + if(action.type === 'otherObj'){ + return {a: {b: 'd'}} + } + + if(action.type === 'deepEqlObj'){ + return cloneDeep(state) + } + + return state + } + + let store + let updateInfos + + beforeEach(() => { + store = createStore(rootReducer, initialState) + updateInfos = [] + whyDidYouRender(React, { + notifier: updateInfo => updateInfos.push(updateInfo) + }) + }) + + afterEach(() => { + if(React.__REVERT_WHY_DID_YOU_RENDER__){ + React.__REVERT_WHY_DID_YOU_RENDER__() + } + }) + + test('no connected component', () => { + store.dispatch({type: 'whatever'}) + expect(store.getState()).toBe(initialState) + expect(store.getState().a.b).toBe('c') + + store.dispatch({type: 'deepEqlObj'}) + expect(store.getState()).not.toBe(initialState) + expect(store.getState()).toEqual(initialState) + expect(store.getState().a.b).toBe('c') + + store.dispatch({type: 'otherObj'}) + expect(store.getState()).not.toBe(initialState) + expect(store.getState()).not.toEqual(initialState) + expect(store.getState().a.b).toBe('d') + }) + + test('simple connect', () => { + const SimpleComponent = ({a}) => ( +
{`{a.b.c} is: ${a.b.c}`}
+ ) + + const ConnectedSimpleComponent = connect( + state => ({a: state.a}) + )(SimpleComponent) + ConnectedSimpleComponent.whyDidYouRender = true + + const Main = () => ( + + + + ) + + rtl.render(
) + + expect(updateInfos).toHaveLength(0) + }) + + test('same state after dispatch', () => { + const SimpleComponent = ({b}) => ( +
{b}
+ ) + + const ConnectedSimpleComponent = connect( + state => ({b: state.a.b}) + )(SimpleComponent) + ConnectedSimpleComponent.whyDidYouRender = true + + const Main = () => ( + + + + ) + + const tester = rtl.render(
) + + expect(store.getState().a.b).toBe('c') + expect(tester.getByTestId('foo')).toHaveTextContent('c') + + store.dispatch({type: 'sameObj'}) + + expect(store.getState().a.b).toBe('c') + expect(tester.getByTestId('foo')).toHaveTextContent('c') + + expect(updateInfos).toHaveLength(0) + }) + + test.skip('deep equals state after dispatch', () => { + const SimpleComponent = ({a}) => ( +
{a.b}
+ ) + + const ConnectedSimpleComponent = connect( + state => ({a: state.a}) + )(SimpleComponent) + ConnectedSimpleComponent.whyDidYouRender = true + + const Main = () => ( + + + + ) + + const tester = rtl.render(
) + + expect(store.getState().a.b).toBe('c') + expect(tester.getByTestId('foo')).toHaveTextContent('c') + + store.dispatch({type: 'deepEqlObj'}) + + expect(store.getState().a.b).toBe('c') + expect(tester.getByTestId('foo')).toHaveTextContent('c') + + // TODO: what do we even expect? + expect(updateInfos).toHaveLength(1) + }) +}) diff --git a/src/whyDidYouRender.js b/src/whyDidYouRender.js index d7c80b9..4e6779d 100644 --- a/src/whyDidYouRender.js +++ b/src/whyDidYouRender.js @@ -1,4 +1,4 @@ -import {defaults, omit, get, mapValues} from 'lodash' +import {defaults, get, mapValues} from 'lodash' import normalizeOptions from './normalizeOptions' import getDisplayName from './getDisplayName' @@ -80,7 +80,9 @@ function patchFunctionalComponent(FunctionalComponent, displayName, React, optio } function patchMemoComponent(MemoComponent, displayName, React, options){ - function InnerWDYRMemoizedFunctionalComponent(nextProps){ + const {type: WrappedFunctionalComponent} = MemoComponent + + function WDYRWrappedByMemoFunctionalComponent(nextProps){ const ref = React.useRef() const prevProps = ref.current @@ -102,15 +104,14 @@ function patchMemoComponent(MemoComponent, displayName, React, options){ } } - return MemoComponent.type(nextProps) + return WrappedFunctionalComponent(nextProps) } - const WDYRMemoizedFunctionalComponent = React.memo(InnerWDYRMemoizedFunctionalComponent) - - const MemoComponentExtra = omit(MemoComponent, Object.keys(WDYRMemoizedFunctionalComponent)) + WDYRWrappedByMemoFunctionalComponent.displayName = getDisplayName(WrappedFunctionalComponent) + WDYRWrappedByMemoFunctionalComponent.ComponentForHooksTracking = MemoComponent + defaults(WDYRWrappedByMemoFunctionalComponent, WrappedFunctionalComponent) - InnerWDYRMemoizedFunctionalComponent.displayName = displayName - defaults(InnerWDYRMemoizedFunctionalComponent, MemoComponentExtra) + const WDYRMemoizedFunctionalComponent = React.memo(WDYRWrappedByMemoFunctionalComponent, MemoComponent.compare) WDYRMemoizedFunctionalComponent.displayName = displayName defaults(WDYRMemoizedFunctionalComponent, MemoComponent) @@ -121,7 +122,9 @@ function patchMemoComponent(MemoComponent, displayName, React, options){ function trackHookChanges(hookName, {path: hookPath}, hookResult, React, options){ const nextHook = hookResult - const Component = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner.current.type + const ComponentHookDispatchedFrom = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner.current.type + + const Component = ComponentHookDispatchedFrom.ComponentForHooksTracking || ComponentHookDispatchedFrom const displayName = getDisplayName(Component) const isShouldTrack = shouldTrack(Component, displayName, options) diff --git a/yarn.lock b/yarn.lock index 2966297..91f36cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -790,6 +790,13 @@ "@babel/plugin-transform-react-jsx-self" "^7.0.0" "@babel/plugin-transform-react-jsx-source" "^7.0.0" +"@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.3": + version "7.4.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.3.tgz#79888e452034223ad9609187a0ad1fe0d2ad4bdc" + integrity sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA== + dependencies: + regenerator-runtime "^0.13.2" + "@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.1.2", "@babel/template@^7.2.2": version "7.2.2" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907" @@ -1041,6 +1048,11 @@ "@types/istanbul-lib-coverage" "^2.0.0" "@types/yargs" "^12.0.9" +"@sheerun/mutationobserver-shim@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#8013f2af54a2b7d735f71560ff360d3a8176a87b" + integrity sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q== + "@types/babel__core@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.0.tgz#710f2487dda4dcfd010ca6abb2b4dc7394365c51" @@ -1980,6 +1992,21 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= + +css@^2.2.3: + version "2.2.4" + resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" + integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== + dependencies: + inherits "^2.0.3" + source-map "^0.6.1" + source-map-resolve "^0.5.2" + urix "^0.1.0" + cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": version "0.3.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.4.tgz#8cd52e8a3acfd68d3aed38ee0a640177d2f9d797" @@ -2146,6 +2173,16 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-testing-library@^3.19.0: + version "3.19.1" + resolved "https://registry.yarnpkg.com/dom-testing-library/-/dom-testing-library-3.19.1.tgz#ccde9044cacd6d95e5e5629b9cdd2f4de3ea5e1d" + integrity sha512-cv5k+OiDhmdjHimDcal5Yz6yHv6nXwhzW3wyXrFeIGk7jSdqqFxqzhjriPvYMrV8XNNZHUDwp0dTu6SeCtO/4w== + dependencies: + "@babel/runtime" "^7.3.4" + "@sheerun/mutationobserver-shim" "^0.3.2" + pretty-format "^24.5.0" + wait-for-expect "^1.1.0" + dom-walk@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" @@ -3129,6 +3166,11 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -3584,7 +3626,7 @@ jest-config@^24.7.1: pretty-format "^24.7.0" realpath-native "^1.1.0" -jest-diff@^24.7.0: +jest-diff@^24.0.0, jest-diff@^24.7.0: version "24.7.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.7.0.tgz#5d862899be46249754806f66e5729c07fcb3580f" integrity sha512-ULQZ5B1lWpH70O4xsANC4tf4Ko6RrpwhE3PtG6ERjMg1TiYTC2Wp4IntJVGro6a8HG9luYHhhmF4grF0Pltckg== @@ -3601,6 +3643,20 @@ jest-docblock@^24.3.0: dependencies: detect-newline "^2.1.0" +jest-dom@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/jest-dom/-/jest-dom-3.1.3.tgz#9490de549c02366fe586f23bdafffd8374bd1d65" + integrity sha512-V9LdySiA74/spcAKEG3FRMRKnisKlcYr3EeCNYI4n7CWNE7uYg5WoBUHeGXirjWjRYLLZ5vx8rUaR/6x6o75oQ== + dependencies: + chalk "^2.4.1" + css "^2.2.3" + css.escape "^1.5.1" + jest-diff "^24.0.0" + jest-matcher-utils "^24.0.0" + lodash "^4.17.11" + pretty-format "^24.0.0" + redent "^2.0.0" + jest-each@^24.7.1: version "24.7.1" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.7.1.tgz#fcc7dda4147c28430ad9fb6dc7211cd17ab54e74" @@ -3688,7 +3744,7 @@ jest-leak-detector@^24.7.0: dependencies: pretty-format "^24.7.0" -jest-matcher-utils@^24.7.0: +jest-matcher-utils@^24.0.0, jest-matcher-utils@^24.7.0: version "24.7.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.7.0.tgz#bbee1ff37bc8b2e4afcaabc91617c1526af4bcd4" integrity sha512-158ieSgk3LNXeUhbVJYRXyTPSCqNgVXOp/GT7O94mYd3pk/8+odKTyR1JLtNOQSPzNi8NFYVONtvSWA/e1RDXg== @@ -4141,7 +4197,7 @@ lodash@4.17.11, lodash@^4, lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -4893,7 +4949,7 @@ preserve@^0.2.0: resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= -pretty-format@^24.7.0: +pretty-format@^24.0.0, pretty-format@^24.5.0, pretty-format@^24.7.0: version "24.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.7.0.tgz#d23106bc2edcd776079c2daa5da02bcb12ed0c10" integrity sha512-apen5cjf/U4dj7tHetpC7UEFCvtAgnNZnBDkfPv3fokzIqyOJckAG9OlAPC1BlFALnqT/lGB2tl9EJjlK6eCsA== @@ -4946,6 +5002,15 @@ prop-types@^15.6.1, prop-types@^15.6.2: loose-envify "^1.3.1" object-assign "^4.1.1" +prop-types@^15.7.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + proxy-addr@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" @@ -5046,7 +5111,7 @@ react-is@^16.7.0, react-is@^16.8.4: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.4.tgz#90f336a68c3a29a096a3d648ab80e87ec61482a2" integrity sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA== -react-is@^16.8.6: +react-is@^16.8.1, react-is@^16.8.6: version "16.8.6" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== @@ -5056,6 +5121,18 @@ react-lifecycles-compat@^3.0.4: resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== +react-redux@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.0.2.tgz#34b280a3482aaf60e7d4a504b1295165cbe6b86a" + integrity sha512-uKRuMgQt8dWbcz0U75oFK5tDo3boyAKrqvf/j94vpqRFFZfyDDy4kofUgloFIGyuKTq2Zz51zgK9RzOTFXk5ew== + dependencies: + "@babel/runtime" "^7.4.3" + hoist-non-react-statics "^3.3.0" + invariant "^2.2.4" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^16.8.6" + react-test-renderer@^16.8.6: version "16.8.6" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.8.6.tgz#188d8029b8c39c786f998aa3efd3ffe7642d5ba1" @@ -5066,6 +5143,14 @@ react-test-renderer@^16.8.6: react-is "^16.8.6" scheduler "^0.13.6" +react-testing-library@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/react-testing-library/-/react-testing-library-6.1.2.tgz#f6bba6eeecedac736eb00b22b4c70bae04535a4f" + integrity sha512-z69lhRDGe7u/NOjDCeFRoe1cB5ckJ4656n0tj/Fdcr6OoBUu7q9DBw0ftR7v5i3GRpdSWelnvl+feZFOyXyxwg== + dependencies: + "@babel/runtime" "^7.4.2" + dom-testing-library "^3.19.0" + react@^16.8.6: version "16.8.6" resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" @@ -5131,6 +5216,22 @@ realpath-native@^1.1.0: dependencies: util.promisify "^1.0.0" +redent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" + integrity sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo= + dependencies: + indent-string "^3.0.0" + strip-indent "^2.0.0" + +redux@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5" + integrity sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg== + dependencies: + loose-envify "^1.4.0" + symbol-observable "^1.2.0" + regenerate-unicode-properties@^8.0.2: version "8.0.2" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.0.2.tgz#7b38faa296252376d363558cfbda90c9ce709662" @@ -5148,6 +5249,11 @@ regenerator-runtime@^0.12.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== +regenerator-runtime@^0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" + integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA== + regenerator-transform@^0.13.4: version "0.13.4" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.4.tgz#18f6763cf1382c69c36df76c6ce122cc694284fb" @@ -5670,7 +5776,7 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -source-map-resolve@^0.5.0: +source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== @@ -5887,6 +5993,11 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= + strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -5913,6 +6024,11 @@ supports-color@^6.0.0, supports-color@^6.1.0: dependencies: has-flag "^3.0.0" +symbol-observable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + symbol-tree@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" @@ -6220,6 +6336,11 @@ w3c-hr-time@^1.0.1: dependencies: browser-process-hrtime "^0.1.2" +wait-for-expect@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.1.1.tgz#9cd10e07d52810af9e0aaf509872e38f3c3d81ae" + integrity sha512-vd9JOqqEcBbCDhARWhW85ecjaEcfBLuXgVBqatfS3iw6oU4kzAcs+sCNjF+TC9YHPImCW7ypsuQc+htscIAQCw== + walker@^1.0.7, walker@~1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"