0

我有这个组件

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 被调用一次并失败

给你日志

提前致谢!

4

0 回答 0