我有这个组件
import React from 'react'
import { useActor } from '@xstate/react'
import { FormattedMessage } from 'react-intl'
import propTypes from 'prop-types'
const Checkbox = ({ checkboxMachine, label, onClick, className }) => {
const [state] = useActor(checkboxMachine)
console.log('checkbox:state', state.value)
return (
<div
className={`flex flex-col items-center cursor-pointer ${className}`}
onClick={onClick}
>
<span
className='dark:text-green-300'
>
<FormattedMessage
id={label}
/>
</span>
<input
type='checkbox'
className='w-0 h-0'
/>
<div
data-testid="checkbox-child-3"
className={`w-8 h-4 rounded-md duration-500 ${state.value === 'checked' ? 'bg-green-300' : 'bg-gray-400'}`}
>
<div
className={`bg-white w-4 h-4 rounded-full duration-500 ${state.value === 'checked' && 'transform translate-x-full'}`}
/>
</div>
</div>
)
}
我有这台机器
import { createMachine } from 'xstate'
import guards from './guards'
import actions from './actions'
import states from './config/states'
import events from './config/events'
export default createMachine({
id: 'checkbox',
initial: states.UNCHECKED,
states : {
[states.UNCHECKED]: {
on: {
[events.TOGGLE]: [
{
cond: guards.shouldChangeThemeMode,
actions: [actions.changeThemeMode],
target: states.CHECKED,
},
],
},
meta: {
test: ({ getByTestId }) => {
console.log('unchecked:assertion')
const checkbox = getByTestId('checkbox-child-3')
expect(checkbox).toHaveClass('w-8 h-4 rounded-md duration-500 bg-gray-400')
},
},
},
[states.CHECKED]: {
on: {
[events.TOGGLE]: [
{
cond: guards.shouldChangeThemeMode,
actions: [actions.changeThemeMode],
target: states.UNCHECKED,
},
],
},
meta: {
test: ({ getByTestId }) => {
console.log('checked:assertion')
const checkbox = getByTestId('checkbox-child-3')
expect(checkbox).toHaveClass('w-8 h-4 rounded-md duration-500 bg-green-300')
},
},
},
},
})
我有这个后卫
const shouldChangeThemeMode = (context, event) => {
console.log('guard:shouldChangeThemeMode', event.action === actionTypes.CHANGE_THEME_MODE)
return event.action === actionTypes.CHANGE_THEME_MODE
}
我有这个动作
const changeThemeMode = () => {
console.log('action:changeThemeMode')
document.documentElement.classList.toggle('dark')
}
我有一个自定义渲染
import React from 'react'
import { IntlProvider, } from 'react-intl'
import HelmetProvider from 'react-navi-helmet-async'
import SpinnerProvider from 'atoms/GlobalSpinner'
import { getMessage, } from 'internationalization/index.js'
import { render as originalRender } from '@testing-library/react'
import 'styles/main.scss'
const render = (ui, { locale, ...renderOptions } = {}) => {
const Wrapper = ({ children }) => {
return (
<SpinnerProvider>
<IntlProvider locale={locale} messages={getMessage()}>
<HelmetProvider>
{children}
</HelmetProvider>
</IntlProvider>
</SpinnerProvider>
)
}
return originalRender(ui, { wrapper: Wrapper, ...renderOptions })
}
export * from '@testing-library/react'
export { render }
最后我有我的测试文件
import React from 'react'
import machine from './index'
import { createModel } from '@xstate/test'
import { render, cleanup } from 'root/jest.utils'
import Checkbox from 'atoms/Checkbox'
import { spawn } from 'xstate'
import actions from 'stateMachines/atoms/checkbox/config/actionTypes'
import events from 'stateMachines/atoms/checkbox/config/events'
const machineModel = createModel(machine, {
events: {
[events.TOGGLE]: {
cases: [
{
action: actions.CHANGE_THEME_MODE,
},
],
},
},
})
describe('checkbox', () => {
const testPlans = machineModel.getSimplePathPlans()
testPlans.forEach((plan) => {
describe(plan.description, () => {
afterEach(cleanup)
plan.paths.forEach((path) => {
it(path.description, () => {
const rendered = render(<Checkbox
checkboxMachine={spawn(machine, 'checkbox')}
label={'home.txt2'}
onClick={() => {}}
/>, { locale: 'en' })
return path.test(rendered)
})
})
})
})
describe('coverage', () => {
it('should have full coverage', () => {
machineModel.testCoverage({
filter: (stateNode) => !stateNode.meta
})
})
})
})
当我跑步时,npm test
我注意到以下内容:
- 在触发 TOGGLE 事件之前,守卫被调用了两次
- 该动作永远不会被调用
- xstate 从不改变我的复选框组件的状态
- UNCHECKED 状态的 meta.test 被调用了两次并且可以正常工作
- CHECKED 状态的 meta.test 被调用一次并失败
提前致谢!