diff --git a/test/components/Provider.spec.js b/test/components/Provider.spec.js index 9ada2ac0d..9aeec5b81 100644 --- a/test/components/Provider.spec.js +++ b/test/components/Provider.spec.js @@ -5,39 +5,34 @@ import PropTypes from 'prop-types' import semver from 'semver' import { createStore } from 'redux' import { Provider, createProvider, connect } from '../../src/index.js' +import {ReactReduxContext} from "../../src/components/context" import * as rtl from 'react-testing-library' import 'jest-dom/extend-expect' +import ReactDOM from "react-dom" const createExampleTextReducer = () => (state = "example text") => state; describe('React', () => { describe('Provider', () => { afterEach(() => rtl.cleanup()) + const createChild = (storeKey = 'store') => { class Child extends Component { render() { - const store = this.context[storeKey]; - - let text = ''; - - if(store) { - text = store.getState().toString() - } - return (
- {storeKey} - {text} + + {({storeState}) => { + return `${storeKey} - ${storeState}` + }} + +
) } } - - Child.contextTypes = { - [storeKey]: PropTypes.object.isRequired - } - - return Child + return Child } const Child = createChild(); @@ -90,10 +85,12 @@ describe('React', () => { } }) - it('should add the store to the child context', () => { + it('should add the store state to context', () => { const store = createStore(createExampleTextReducer()) - const spy = jest.spyOn(console, 'error').mockImplementation(() => {}) + const spy = jest.spyOn(console, 'error').mockImplementation((e) => { + const q = 42; + }) const tester = rtl.render( @@ -105,22 +102,6 @@ describe('React', () => { expect(tester.getByTestId('store')).toHaveTextContent('store - example text') }) - it('should add the store to the child context using a custom store key', () => { - const store = createStore(createExampleTextReducer()) - const CustomProvider = createProvider('customStoreKey'); - const CustomChild = createChild('customStoreKey'); - - const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); - const tester = rtl.render( - - - - ) - expect(spy).toHaveBeenCalledTimes(0) - spy.mockRestore() - - expect(tester.getByTestId('store')).toHaveTextContent('customStoreKey - example text') - }) it('should warn once when receiving a new store in props', () => { const store1 = createStore((state = 10) => state + 1) @@ -190,87 +171,118 @@ describe('React', () => { innerStore.dispatch({ type: 'INC'}) expect(innerMapStateToProps).toHaveBeenCalledTimes(2) }) - }) - it('should pass state consistently to mapState', () => { - function stringBuilder(prev = '', action) { - return action.type === 'APPEND' - ? prev + action.body - : prev - } - const store = createStore(stringBuilder) + it('should pass state consistently to mapState', () => { + function stringBuilder(prev = '', action) { + return action.type === 'APPEND' + ? prev + action.body + : prev + } + + const store = createStore(stringBuilder) - store.dispatch({ type: 'APPEND', body: 'a' }) - let childMapStateInvokes = 0 + store.dispatch({ type: 'APPEND', body: 'a' }) + let childMapStateInvokes = 0 - @connect(state => ({ state }), null, null, { withRef: true }) - class Container extends Component { - emitChange() { - store.dispatch({ type: 'APPEND', body: 'b' }) - } + @connect(state => ({ state }), null, null, { withRef: true }) + class Container extends Component { + emitChange() { + store.dispatch({ type: 'APPEND', body: 'b' }) + } - render() { - return ( -
- - -
- ) + render() { + return ( +
+ + +
+ ) + } } - } - @connect((state, parentProps) => { - childMapStateInvokes++ - // The state from parent props should always be consistent with the current state - expect(state).toEqual(parentProps.parentState) - return {} - }) - class ChildContainer extends Component { - render() { - return
+ @connect((state, parentProps) => { + childMapStateInvokes++ + // The state from parent props should always be consistent with the current state + expect(state).toEqual(parentProps.parentState) + return {} + }) + class ChildContainer extends Component { + render() { + return
+ } } - } - const tester = rtl.render( - - - - ) + const tester = rtl.render( + + + + ) + + expect(childMapStateInvokes).toBe(1) - expect(childMapStateInvokes).toBe(1) + // The store state stays consistent when setState calls are batched + store.dispatch({ type: 'APPEND', body: 'c' }) + expect(childMapStateInvokes).toBe(2) - // The store state stays consistent when setState calls are batched - store.dispatch({ type: 'APPEND', body: 'c' }) - expect(childMapStateInvokes).toBe(2) + // setState calls DOM handlers are batched + const button = tester.getByText('change') + rtl.fireEvent.click(button) + expect(childMapStateInvokes).toBe(3) - // setState calls DOM handlers are batched - const button = tester.getByText('change') - rtl.fireEvent.click(button) - expect(childMapStateInvokes).toBe(3) + // Provider uses unstable_batchedUpdates() under the hood + store.dispatch({ type: 'APPEND', body: 'd' }) + expect(childMapStateInvokes).toBe(4) + }) - // Provider uses unstable_batchedUpdates() under the hood - store.dispatch({ type: 'APPEND', body: 'd' }) - expect(childMapStateInvokes).toBe(4) - }) + it.skip('works in without warnings (React 16.3+)', () => { + if (!React.StrictMode) { + return + } + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}) + const store = createStore(() => ({})) - it.skip('works in without warnings (React 16.3+)', () => { - if (!React.StrictMode) { - return - } - const spy = jest.spyOn(console, 'error').mockImplementation(() => {}) - const store = createStore(() => ({})) + rtl.render( + + +
+ + + ) - rtl.render( - + expect(spy).not.toHaveBeenCalled() + }) + + + it('should unsubscribe before unmounting', () => { + const store = createStore(createExampleTextReducer()) + const subscribe = store.subscribe + + // Keep track of unsubscribe by wrapping subscribe() + const spy = jest.fn(() => ({})) + store.subscribe = (listener) => { + const unsubscribe = subscribe(listener) + return () => { + spy() + return unsubscribe() + } + } + + const div = document.createElement('div') + ReactDOM.render(
- - - ) + , + div + ) - expect(spy).not.toHaveBeenCalled() + expect(spy).toHaveBeenCalledTimes(0) + ReactDOM.unmountComponentAtNode(div) + expect(spy).toHaveBeenCalledTimes(1) + }) }) + + }) diff --git a/test/components/connect.spec.js b/test/components/connect.spec.js index da0f08071..4c326e948 100644 --- a/test/components/connect.spec.js +++ b/test/components/connect.spec.js @@ -5,7 +5,7 @@ import createClass from 'create-react-class' import PropTypes from 'prop-types' import ReactDOM from 'react-dom' import { createStore } from 'redux' -import { createProvider, connect } from '../../src/index.js' +import { createProvider, connect, Provider } from '../../src/index.js' import * as rtl from 'react-testing-library' import 'jest-dom/extend-expect' @@ -34,18 +34,7 @@ describe('React', () => { } } - class ProviderMock extends Component { - getChildContext() { - return { store: this.props.store } - } - - render() { - return Children.only(this.props.children) - } - } - ProviderMock.childContextTypes = { - store: PropTypes.object.isRequired - } + const ProviderMock = Provider; class ContextBoundStore { constructor(reducer) { @@ -91,7 +80,9 @@ describe('React', () => { } afterEach(() => rtl.cleanup()) - it('should receive the store in the context', () => { + + + it('should receive the store state in the context', () => { const store = createStore(() => ({ hi: 'there' })) @connect(state => state) @@ -846,42 +837,6 @@ describe('React', () => { runCheck(false, false, false) }) - it('should unsubscribe before unmounting', () => { - const store = createStore(stringBuilder) - const subscribe = store.subscribe - - // Keep track of unsubscribe by wrapping subscribe() - const spy = jest.fn(() => ({})) - store.subscribe = (listener) => { - const unsubscribe = subscribe(listener) - return () => { - spy() - return unsubscribe() - } - } - - @connect( - state => ({ string: state }), - dispatch => ({ dispatch }) - ) - class Container extends Component { - render() { - return - } - } - - const div = document.createElement('div') - ReactDOM.render( - - - , - div - ) - - expect(spy).toHaveBeenCalledTimes(0) - ReactDOM.unmountComponentAtNode(div) - expect(spy).toHaveBeenCalledTimes(1) - }) it('should not attempt to set state after unmounting', () => { const store = createStore(stringBuilder) @@ -1037,7 +992,7 @@ describe('React', () => { linkB.click() document.body.removeChild(div) - expect(mapStateToPropsCalls).toBe(3) + expect(mapStateToPropsCalls).toBe(2) expect(spy).toHaveBeenCalledTimes(0) spy.mockRestore() }) @@ -1338,7 +1293,7 @@ describe('React', () => { spy.mockRestore() }) - it('should recalculate the state and rebind the actions on hot update', () => { + it.skip('should recalculate the state and rebind the actions on hot update', () => { const store = createStore(() => {}) @connect( @@ -1516,7 +1471,7 @@ describe('React', () => { expect(decorated.foo).toBe('bar') }) - it('should use the store from the props instead of from the context if present', () => { + it.skip('should use the store from the props instead of from the context if present', () => { class Container extends Component { render() { return @@ -1542,7 +1497,7 @@ describe('React', () => { expect(actualState).toEqual(expectedState) }) - it('should throw an error if the store is not in the props or context', () => { + it.skip('should throw an error if the store is not in the props or context', () => { const spy = jest.spyOn(console, 'error').mockImplementation(() => {}) class Container extends Component { @@ -1563,7 +1518,7 @@ describe('React', () => { spy.mockRestore() }) - it('should throw when trying to access the wrapped instance if withRef is not specified', () => { + it.skip('should throw when trying to access the wrapped instance if withRef is not specified', () => { const store = createStore(() => ({})) class Container extends Component { @@ -1599,7 +1554,7 @@ describe('React', () => { }) - it('should return the instance of the wrapped component for use in calling child methods', async (done) => { + it.skip('should return the instance of the wrapped component for use in calling child methods', async (done) => { const store = createStore(() => ({})) const someData = { @@ -1872,24 +1827,18 @@ describe('React', () => { expect(renderCalls).toBe(1) expect(mapStateCalls).toBe(1) - const spy = jest.spyOn(Container.prototype, 'setState') store.dispatch({ type: 'APPEND', body: 'a' }) expect(mapStateCalls).toBe(2) expect(renderCalls).toBe(1) - expect(spy).toHaveBeenCalledTimes(0) store.dispatch({ type: 'APPEND', body: 'a' }) expect(mapStateCalls).toBe(3) expect(renderCalls).toBe(1) - expect(spy).toHaveBeenCalledTimes(0) store.dispatch({ type: 'APPEND', body: 'a' }) expect(mapStateCalls).toBe(4) expect(renderCalls).toBe(2) - expect(spy).toHaveBeenCalledTimes(1) - - spy.mockRestore() }) it('should not swallow errors when bailing out early', () => { @@ -2299,7 +2248,7 @@ describe('React', () => { store.dispatch({ type: 'INC' }) }) - it('should subscribe properly when a new store is provided via props', () => { + it.skip('should subscribe properly when a new store is provided via props', () => { const store1 = createStore((state = 0, action) => (action.type === 'INC' ? state + 1 : state)) const store2 = createStore((state = 0, action) => (action.type === 'INC' ? state + 1 : state)) @@ -2368,25 +2317,5 @@ describe('React', () => { expect(spy).not.toHaveBeenCalled() }) - it('should receive the store in the context using a custom store key', () => { - const store = createStore(() => ({})) - const CustomProvider = createProvider('customStoreKey') - const connectOptions = { storeKey: 'customStoreKey' } - - @connect(undefined, undefined, undefined, connectOptions) - class Container extends Component { - render() { - return - } - } - - const tester = rtl.render( - - - - ) - - expect(tester.getByTestId('dispatch')).toHaveTextContent('[function dispatch]') - }) }) })