Skip to content

Commit ed7a910

Browse files
committed
Rework tests based on changes to connect and Provider
1 parent a64103c commit ed7a910

File tree

2 files changed

+119
-178
lines changed

2 files changed

+119
-178
lines changed

test/components/Provider.spec.js

+107-95
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,34 @@ import PropTypes from 'prop-types'
55
import semver from 'semver'
66
import { createStore } from 'redux'
77
import { Provider, createProvider, connect } from '../../src/index.js'
8+
import {ReactReduxContext} from "../../src/components/context"
89
import * as rtl from 'react-testing-library'
910
import 'jest-dom/extend-expect'
11+
import ReactDOM from "react-dom"
1012

1113
const createExampleTextReducer = () => (state = "example text") => state;
1214

1315
describe('React', () => {
1416
describe('Provider', () => {
1517
afterEach(() => rtl.cleanup())
18+
1619
const createChild = (storeKey = 'store') => {
1720
class Child extends Component {
1821
render() {
19-
const store = this.context[storeKey];
20-
21-
let text = '';
22-
23-
if(store) {
24-
text = store.getState().toString()
25-
}
26-
2722
return (
2823
<div data-testid="store">
29-
{storeKey} - {text}
24+
<ReactReduxContext.Consumer>
25+
{({storeState}) => {
26+
return `${storeKey} - ${storeState}`
27+
}}
28+
</ReactReduxContext.Consumer>
29+
3030
</div>
3131
)
3232
}
3333
}
3434

35-
36-
Child.contextTypes = {
37-
[storeKey]: PropTypes.object.isRequired
38-
}
39-
40-
return Child
35+
return Child
4136
}
4237
const Child = createChild();
4338

@@ -90,10 +85,12 @@ describe('React', () => {
9085
}
9186
})
9287

93-
it('should add the store to the child context', () => {
88+
it('should add the store state to context', () => {
9489
const store = createStore(createExampleTextReducer())
9590

96-
const spy = jest.spyOn(console, 'error').mockImplementation(() => {})
91+
const spy = jest.spyOn(console, 'error').mockImplementation((e) => {
92+
const q = 42;
93+
})
9794
const tester = rtl.render(
9895
<Provider store={store}>
9996
<Child />
@@ -105,22 +102,6 @@ describe('React', () => {
105102
expect(tester.getByTestId('store')).toHaveTextContent('store - example text')
106103
})
107104

108-
it('should add the store to the child context using a custom store key', () => {
109-
const store = createStore(createExampleTextReducer())
110-
const CustomProvider = createProvider('customStoreKey');
111-
const CustomChild = createChild('customStoreKey');
112-
113-
const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
114-
const tester = rtl.render(
115-
<CustomProvider store={store}>
116-
<CustomChild />
117-
</CustomProvider>
118-
)
119-
expect(spy).toHaveBeenCalledTimes(0)
120-
spy.mockRestore()
121-
122-
expect(tester.getByTestId('store')).toHaveTextContent('customStoreKey - example text')
123-
})
124105

125106
it('should warn once when receiving a new store in props', () => {
126107
const store1 = createStore((state = 10) => state + 1)
@@ -190,87 +171,118 @@ describe('React', () => {
190171
innerStore.dispatch({ type: 'INC'})
191172
expect(innerMapStateToProps).toHaveBeenCalledTimes(2)
192173
})
193-
})
194174

195-
it('should pass state consistently to mapState', () => {
196-
function stringBuilder(prev = '', action) {
197-
return action.type === 'APPEND'
198-
? prev + action.body
199-
: prev
200-
}
201175

202-
const store = createStore(stringBuilder)
176+
it('should pass state consistently to mapState', () => {
177+
function stringBuilder(prev = '', action) {
178+
return action.type === 'APPEND'
179+
? prev + action.body
180+
: prev
181+
}
182+
183+
const store = createStore(stringBuilder)
203184

204-
store.dispatch({ type: 'APPEND', body: 'a' })
205-
let childMapStateInvokes = 0
185+
store.dispatch({ type: 'APPEND', body: 'a' })
186+
let childMapStateInvokes = 0
206187

207-
@connect(state => ({ state }), null, null, { withRef: true })
208-
class Container extends Component {
209-
emitChange() {
210-
store.dispatch({ type: 'APPEND', body: 'b' })
211-
}
188+
@connect(state => ({ state }), null, null, { withRef: true })
189+
class Container extends Component {
190+
emitChange() {
191+
store.dispatch({ type: 'APPEND', body: 'b' })
192+
}
212193

213-
render() {
214-
return (
215-
<div>
216-
<button onClick={this.emitChange.bind(this)}>change</button>
217-
<ChildContainer parentState={this.props.state} />
218-
</div>
219-
)
194+
render() {
195+
return (
196+
<div>
197+
<button onClick={this.emitChange.bind(this)}>change</button>
198+
<ChildContainer parentState={this.props.state} />
199+
</div>
200+
)
201+
}
220202
}
221-
}
222203

223-
@connect((state, parentProps) => {
224-
childMapStateInvokes++
225-
// The state from parent props should always be consistent with the current state
226-
expect(state).toEqual(parentProps.parentState)
227-
return {}
228-
})
229-
class ChildContainer extends Component {
230-
render() {
231-
return <div />
204+
@connect((state, parentProps) => {
205+
childMapStateInvokes++
206+
// The state from parent props should always be consistent with the current state
207+
expect(state).toEqual(parentProps.parentState)
208+
return {}
209+
})
210+
class ChildContainer extends Component {
211+
render() {
212+
return <div />
213+
}
232214
}
233-
}
234215

235-
const tester = rtl.render(
236-
<Provider store={store}>
237-
<Container />
238-
</Provider>
239-
)
216+
const tester = rtl.render(
217+
<Provider store={store}>
218+
<Container />
219+
</Provider>
220+
)
221+
222+
expect(childMapStateInvokes).toBe(1)
240223

241-
expect(childMapStateInvokes).toBe(1)
224+
// The store state stays consistent when setState calls are batched
225+
store.dispatch({ type: 'APPEND', body: 'c' })
226+
expect(childMapStateInvokes).toBe(2)
242227

243-
// The store state stays consistent when setState calls are batched
244-
store.dispatch({ type: 'APPEND', body: 'c' })
245-
expect(childMapStateInvokes).toBe(2)
228+
// setState calls DOM handlers are batched
229+
const button = tester.getByText('change')
230+
rtl.fireEvent.click(button)
231+
expect(childMapStateInvokes).toBe(3)
246232

247-
// setState calls DOM handlers are batched
248-
const button = tester.getByText('change')
249-
rtl.fireEvent.click(button)
250-
expect(childMapStateInvokes).toBe(3)
233+
// Provider uses unstable_batchedUpdates() under the hood
234+
store.dispatch({ type: 'APPEND', body: 'd' })
235+
expect(childMapStateInvokes).toBe(4)
236+
})
251237

252-
// Provider uses unstable_batchedUpdates() under the hood
253-
store.dispatch({ type: 'APPEND', body: 'd' })
254-
expect(childMapStateInvokes).toBe(4)
255-
})
256238

239+
it.skip('works in <StrictMode> without warnings (React 16.3+)', () => {
240+
if (!React.StrictMode) {
241+
return
242+
}
243+
const spy = jest.spyOn(console, 'error').mockImplementation(() => {})
244+
const store = createStore(() => ({}))
257245

258-
it.skip('works in <StrictMode> without warnings (React 16.3+)', () => {
259-
if (!React.StrictMode) {
260-
return
261-
}
262-
const spy = jest.spyOn(console, 'error').mockImplementation(() => {})
263-
const store = createStore(() => ({}))
246+
rtl.render(
247+
<React.StrictMode>
248+
<Provider store={store}>
249+
<div />
250+
</Provider>
251+
</React.StrictMode>
252+
)
264253

265-
rtl.render(
266-
<React.StrictMode>
254+
expect(spy).not.toHaveBeenCalled()
255+
})
256+
257+
258+
it('should unsubscribe before unmounting', () => {
259+
const store = createStore(createExampleTextReducer())
260+
const subscribe = store.subscribe
261+
262+
// Keep track of unsubscribe by wrapping subscribe()
263+
const spy = jest.fn(() => ({}))
264+
store.subscribe = (listener) => {
265+
const unsubscribe = subscribe(listener)
266+
return () => {
267+
spy()
268+
return unsubscribe()
269+
}
270+
}
271+
272+
const div = document.createElement('div')
273+
ReactDOM.render(
267274
<Provider store={store}>
268275
<div />
269-
</Provider>
270-
</React.StrictMode>
271-
)
276+
</Provider>,
277+
div
278+
)
272279

273-
expect(spy).not.toHaveBeenCalled()
280+
expect(spy).toHaveBeenCalledTimes(0)
281+
ReactDOM.unmountComponentAtNode(div)
282+
expect(spy).toHaveBeenCalledTimes(1)
283+
})
274284
})
275285

286+
287+
276288
})

0 commit comments

Comments
 (0)