Skip to content

Commit

Permalink
test: reducer & effect
Browse files Browse the repository at this point in the history
  • Loading branch information
chnliquan committed Dec 8, 2020
1 parent c4c6bea commit c1c70b5
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 20 deletions.
3 changes: 2 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
module.exports = {
roots: ['<rootDir>'],
collectCoverage: true,
collectCoverageFrom: ['**/*.{ts,tsx}', '!**/node_modules/**'],
collectCoverageFrom: ['src/**/*.{ts,tsx}', '!**/.umi/**'],
coverageDirectory: 'coverage',
testRegex: 'test/(.+)\\.spec\\.(jsx?|tsx?)$',
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
setupFilesAfterEnv: ['./test/setupTests.ts'],
}
91 changes: 91 additions & 0 deletions test/effect.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React from 'react'
import { createStore } from '../src/index'
import { counter } from './helper/model'
import { createHook } from './helper/createHook'
import { Counter, Counter1 } from './helper/CountFunctionComponent'
import { act, fireEvent, render } from '@testing-library/react'
import { store } from './helper/store'

console.error = jest.fn()

describe('effect test', () => {
it('effect should be async function', () => {
const store = createStore({
counter,
})
const { Provider, useModel } = store

const { result } = createHook(Provider, useModel, 'counter')

const increaseAsync = result.current.effects.increaseAsync

expect(typeof increaseAsync().then).toBe('function')
})

it('effect should have expected field', () => {
const store = createStore({
counter,
})
const { Provider, useModel } = store

const { result } = createHook(Provider, useModel, 'counter')

const increaseAsync = result.current.effects.increaseAsync

expect(increaseAsync.loading).toBe(false)
expect(increaseAsync.identifier).toBe(0)
})

it('should set loading to true when the effect is executed, and after execution, it should be set to false', async () => {
const store = createStore({
counter,
})
const { Provider, useModel } = store

const { result, waitForNextUpdate } = createHook(Provider, useModel, 'counter')

const increaseAsync = result.current.effects.increaseAsync

expect(increaseAsync.loading).toBe(false)
increaseAsync()
expect(increaseAsync.loading).toBe(true)

await waitForNextUpdate()

expect(increaseAsync.loading).toBe(false)
expect(result.current.state.count).toBe(1)
})

it('should only rerender when Component depend effect loading', (done) => {
const CounterRender = jest.fn()
const Counter1Render = jest.fn()

// https://spectrum.chat/testing-library/help/is-there-a-way-to-count-the-number-of-times-a-component-gets-rendered~8b8b3f8f-775d-49cc-80fd-baaf40fa37eb
const { getByTestId, queryByText } = render(
<store.Provider>
<Counter onRender={CounterRender} />
<Counter1 onRender={Counter1Render} />
</store.Provider>
)

expect(CounterRender).toBeCalledTimes(1)
expect(Counter1Render).toBeCalledTimes(1)

expect(queryByText('Loading ...')).not.toBeInTheDocument()

act(() => {
fireEvent.click(getByTestId('increaseAsync'))
})

expect(CounterRender).toBeCalledTimes(2)
expect(Counter1Render).toBeCalledTimes(1)

expect(queryByText('Loading ...')).toBeInTheDocument()

setTimeout(() => {
expect(CounterRender).toBeCalledTimes(4)
expect(Counter1Render).toBeCalledTimes(2)
done()
}, 1000)
})
})
33 changes: 31 additions & 2 deletions test/helper/CountFunctionComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
import React from 'react'
import store from './store'
import { store } from './store'

export const Counter: React.FC = () => {
export interface CounterProps {
onRender?: () => void
}

export const Counter: React.FC<CounterProps> = ({ onRender }) => {
const { state, reducers, effects } = store.useModel('counter')

if(onRender) {
onRender()
}

if(effects.increaseAsync.loading) {
return <div>Loading ...</div>
}

return (
<div>
<div data-testid="count">{state.count}</div>
Expand All @@ -13,3 +25,20 @@ export const Counter: React.FC = () => {
</div>
)
}

export const Counter1: React.FC<CounterProps> = ({ onRender }) => {
const { state, reducers, effects } = store.useModel('counter')

if(onRender) {
onRender()
}

return (
<div>
<div data-testid="count1">{state.count}</div>
<div data-testid="increase1" onClick={reducers.increase} />
<div data-testid="decrease1" onClick={reducers.decrease} />
<div data-testid="increaseAsync1" onClick={effects.increaseAsync} />
</div>
)
}
18 changes: 4 additions & 14 deletions test/helper/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ export const config = {
data: {},
},
reducers: {
increase(state) {
increase(state: any) {
state.count++
},
decrease(state) {
decrease(state: any) {
state.count--
},
},
effects: (store, rootStore) => ({
effects: (store: any, rootStore: any) => ({
async increaseAsync() {
await wait(500)
store.reducers.increase()
Expand All @@ -42,17 +42,7 @@ export const defaultModelOptions = {
name: 'counter',
config: {
...config,
effects: {
async increaseAsync() {
await wait(500)
},

async fetchError() {
return new Promise((_, reject) => {
reject('customer error')
})
},
},
effects: {},
},
rootModel: Object.create(null),
autoReset: false,
Expand Down
4 changes: 1 addition & 3 deletions test/helper/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,4 @@ import * as models from './model'

export type RootModel = Models<typeof models>

const store = createStore({ ...models })

export default store
export const store = createStore({ ...models })
166 changes: 166 additions & 0 deletions test/reducer.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { act } from '@testing-library/react-hooks'
import { createStore } from '../src/index'
import { counter } from './helper/model'
import { createHook } from './helper/createHook'

describe('reducer test', () => {
it('customize reducers execution results should be as expected', () => {
const store = createStore({
counter,
})
const { Provider, useModel } = store

const { result } = createHook(Provider, useModel, 'counter')

const increase = result.current.reducers.increase
const decrease = result.current.reducers.decrease

expect(increase.length).toBe(0)

act(() => {
increase()
})
expect(result.current.state.count).toBe(1)

act(() => {
decrease()
})
expect(result.current.state.count).toBe(0)
})

it('should not rerender when the value of mapStateToProps returned not modified', () => {
const store = createStore({
counter,
})
const { Provider, useModel } = store

const { result } = createHook(Provider, useModel, 'counter', state => {
return {
count: state.count,
}
})

const increase = result.current.reducers.increase
const decrease = result.current.reducers.decrease

expect(result.current.state.count).toBe(0)
expect(result.current.state.data).toBeUndefined()
expect(increase.length).toBe(0)

act(() => {
increase()
})
expect(result.current.state.count).toBe(1)

act(() => {
decrease()
})
expect(result.current.state.count).toBe(0)

act(() => {
result.current.reducers.setValue('data', 1)
})

expect(result.current.state.data).toBeUndefined()
})

it('should provider build-in reducers when no customize passed', () => {
const store = createStore({
counter,
})
const { Provider, useModel } = store

const { result } = createHook(Provider, useModel, 'counter')

const setValue = result.current.reducers.setValue
const setValues = result.current.reducers.setValues
const reset = result.current.reducers.reset

expect(setValue.length).toBe(2)
act(() => {
setValue('count', 1)
})
expect(result.current.state.count).toBe(1)

expect(setValues.length).toBe(1)
act(() => {
setValues({
count: 10,
})
})
expect(result.current.state.count).toBe(10)

expect(reset.length).toBe(1)
act(() => {
reset('count')
})
expect(result.current.state.count).toBe(0)

act(() => {
setValues({
data: 1,
count: 10,
})
})

expect(result.current.state.count).toBe(10)
expect(result.current.state.data).toBe(1)

act(() => {
reset()
})

expect(result.current.state.count).toBe(0)
expect(result.current.state.data).toEqual({})
})

it('should overwrite build-in reducers when customize passed', () => {
const store = createStore({
counter: {
state: {
count: 10,
},
reducers: {
setValue(state, payload) {
state.count = payload + 1
},

setValues(state, partialState) {
Object.keys(partialState).forEach(key => {
state[key] = partialState[key] + 1
})
},

reset(state) {
state.count = 10
},
},
effects: () => ({}),
},
})
const { Provider, useModel } = store

const { result } = createHook(Provider, useModel, 'counter')

const setValue = result.current.reducers.setValue
const setValues = result.current.reducers.setValues
const reset = result.current.reducers.reset

act(() => {
setValue(1)
})
expect(result.current.state.count).toBe(2)

act(() => {
setValues({
count: 10,
})
})
expect(result.current.state.count).toBe(11)

act(() => {
reset()
})
expect(result.current.state.count).toBe(10)
})
})
5 changes: 5 additions & 0 deletions test/setupTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect'

0 comments on commit c1c70b5

Please sign in to comment.