Add test for pack.ts

This commit is contained in:
Jerko Steiner 2019-09-05 12:58:59 +07:00
parent a6a8fd833d
commit ee6a6741ed
3 changed files with 144 additions and 11 deletions

View File

@ -17,8 +17,6 @@ import {ComponentType} from 'react'
* the Actions class might depend on the HTTPClient class, and then it becomes * the Actions class might depend on the HTTPClient class, and then it becomes
* easy to mock it during tests, or swap out different dependencies for * easy to mock it during tests, or swap out different dependencies for
* different applications. * different applications.
*
* @deprecated
*/ */
export abstract class Connector<LocalState> { export abstract class Connector<LocalState> {

View File

@ -0,0 +1,136 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { createStore, Dispatch } from 'redux'
import { pack } from './pack'
import { TStateSelector } from './TStateSelector'
import TestUtils from 'react-dom/test-utils'
describe('pack', () => {
interface IProps {
a: number
b: string
update(a: number, b: string): {
payload: {a: number, b: string},
type: 'CHANGE',
}
c: string[]
}
class PureComponent extends React.PureComponent<IProps> {
update = () => {
this.props.update(1, 'one')
}
render() {
return (
<button onClick={this.update}>
{this.props.a + this.props.b}
</button>
)
return this.props.a + this.props.b
}
}
function FunctionalComponent(props: IProps) {
return (
<button onClick={() => props.update(1, 'one')}>
{props.a + props.b}
</button>
)
}
type LocalState = Omit<IProps, 'c' | 'update'>
interface IState {
localState: LocalState
}
function reduce(
state: IState = {localState: {a: 0, b: ''}},
action: any,
): IState {
switch (action.type) {
case 'CHANGE':
return {
...state,
localState: {
...action.payload,
},
}
default:
return state
}
}
function configurePureComponent<State>(
getLocalState: TStateSelector<State, LocalState>,
) {
return pack(
getLocalState,
(localState: LocalState) => localState,
{
update(a: number, b: string) {
return {
payload: {a, b},
type: 'CHANGE',
}
},
},
PureComponent,
)
}
function configureFunctionalComponent<State>(
getLocalState: TStateSelector<State, LocalState>,
) {
return pack(
getLocalState,
(localState: LocalState) => localState,
{
update(a: number, b: string) {
return {
payload: {a, b},
type: 'CHANGE',
}
},
},
FunctionalComponent,
)
}
const PackedPureComponent = configurePureComponent<IState>(
state => state.localState)
const PackedFunctionalComponent = configureFunctionalComponent<IState>(
state => state.localState)
it('creates a connected component', () => {
const store = createStore(reduce)
const element = document.createElement('div')!
ReactDOM.render(
<Provider store={store}>
<PackedPureComponent c={['test']} />
</Provider>,
element,
)
expect(element.textContent).toBe('0')
TestUtils.Simulate.click(element.querySelector('button')!)
expect(element.textContent).toBe('1one')
})
it('should work with functional components', () => {
const store = createStore(reduce)
const element = document.createElement('div')!
ReactDOM.render(
<Provider store={store}>
<PackedFunctionalComponent c={['test']} />
</Provider>,
element,
)
expect(element.textContent).toBe('0')
TestUtils.Simulate.click(element.querySelector('button')!)
expect(element.textContent).toBe('1one')
})
})

View File

@ -1,5 +1,5 @@
import { ComponentType, PureComponent } from 'react' import { ComponentType, PureComponent } from 'react'
import { connect, Omit, MapDispatchToPropsParam } from 'react-redux' import { connect, Omit, MapDispatchToPropsParam, Matching, GetProps, ResolveThunks } from 'react-redux'
import { Dispatch } from 'redux' import { Dispatch } from 'redux'
import { TStateSelector } from './TStateSelector' import { TStateSelector } from './TStateSelector'
@ -29,22 +29,21 @@ export function pack<
LocalState, LocalState,
State, State,
Props, Props,
StateProps extends Partial<Props>, StateProps,
DispatchProps extends Partial<Props>, DispatchProps,
C extends React.ComponentType<
Matching<StateProps & ResolveThunks<DispatchProps>, GetProps<C>>>
>( >(
getLocalState: TStateSelector<State, LocalState>, getLocalState: TStateSelector<State, LocalState>,
mapStateToProps: (state: LocalState) => StateProps, mapStateToProps: (state: LocalState) => StateProps,
mapDispatchToProps: MapDispatchToPropsParam<DispatchProps, Props>, mapDispatchToProps: MapDispatchToPropsParam<DispatchProps, Props>,
Component: React.ComponentType<Props>, Component: C,
): ComponentType< ) {
Omit<Props, keyof Props & (keyof StateProps | keyof DispatchProps)>
> {
return connect( return connect(
(state: State) => { (state: State) => {
const l = getLocalState(state) const l = getLocalState(state)
return mapStateToProps(l) return mapStateToProps(l)
}, },
mapDispatchToProps, mapDispatchToProps,
)(Component as any) as any )(Component)
} }