import { render, fireEvent, waitFor } from '@testing-library/react' import { mockShowToast } from '~/__mocks__/taroMock' import CancelReasonDialog from '@/components/common/CancelReasonDialog' describe('CancelReasonDialog', () => { const defaultProps = { open: true, onOpenChange: jest.fn(), onConfirm: jest.fn(), loading: false } beforeEach(() => { jest.clearAllMocks() }) it('应该渲染对话框当可见时为true', () => { const { getByText, getAllByText } = render() expect(getByText('取消订单')).toBeTruthy() expect(getByText('请选择或填写取消原因,这将帮助我们改进服务')).toBeTruthy() expect(getByText('我不想买了')).toBeTruthy() expect(getByText('信息填写错误,重新下单')).toBeTruthy() expect(getByText('商家缺货')).toBeTruthy() expect(getByText('价格不合适')).toBeTruthy() // 有多个"其他原因"文本,使用getAllByText expect(getAllByText('其他原因').length).toBeGreaterThan(0) }) it('不应该渲染对话框当open为false时', () => { const { queryByText } = render( ) expect(queryByText('取消订单')).toBeNull() }) it.each([ '我不想买了', '信息填写错误,重新下单', '商家缺货', '价格不合适', '其他原因' ])('应该选择预定义原因 %s 当点击时', async (reason) => { const { getByTestId } = render() const reasonOption = getByTestId(`cancel-reason-${reason}`) fireEvent.click(reasonOption) // 等待状态更新完成 await waitFor(() => { expect(reasonOption).toHaveClass('border-primary') }) const confirmButton = getByTestId('confirm-cancel-button') fireEvent.click(confirmButton) expect(defaultProps.onConfirm).toHaveBeenCalledWith(reason) }) it('应该显示选中状态当点击预定义原因时', async () => { const { getByTestId } = render() // 点击第一个原因 const firstReason = getByTestId('cancel-reason-我不想买了') fireEvent.click(firstReason) // 等待状态更新完成 await waitFor(() => { // 验证选中状态样式 expect(firstReason).toHaveClass('border-primary') expect(firstReason).toHaveClass('bg-primary/10') }) // 点击第二个原因,第一个应该取消选中 const secondReason = getByTestId('cancel-reason-信息填写错误,重新下单') fireEvent.click(secondReason) // 等待状态更新完成 await waitFor(() => { // 第一个原因应该不再有选中状态 expect(firstReason).not.toHaveClass('border-primary') expect(firstReason).not.toHaveClass('bg-primary/10') // 第二个原因应该有选中状态 expect(secondReason).toHaveClass('border-primary') expect(secondReason).toHaveClass('bg-primary/10') }) }) it('应该清除预定义原因选中状态当输入自定义原因时', async () => { const { getByTestId, getByPlaceholderText } = render() // 先点击一个预定义原因 const reasonOption = getByTestId('cancel-reason-我不想买了') fireEvent.click(reasonOption) // 等待选中状态更新 await waitFor(() => { // 验证有选中状态 expect(reasonOption).toHaveClass('border-primary') expect(reasonOption).toHaveClass('bg-primary/10') }) // 输入自定义原因 const input = getByPlaceholderText('请输入其他取消原因...') fireEvent.input(input, { target: { value: '自定义取消原因' } }) // 等待选中状态清除 await waitFor(() => { // 预定义原因的选中状态应该被清除 expect(reasonOption).not.toHaveClass('border-primary') expect(reasonOption).not.toHaveClass('bg-primary/10') }) }) it('应该模拟用户实际点击流程', async () => { const { getByTestId } = render() // 点击一个原因 const reasonOption = getByTestId('cancel-reason-商家缺货') fireEvent.click(reasonOption) // 等待选中状态 await waitFor(() => { expect(reasonOption).toHaveClass('border-primary') expect(reasonOption).toHaveClass('bg-primary/10') }) // 立即点击确认按钮 const confirmButton = getByTestId('confirm-cancel-button') fireEvent.click(confirmButton) // 应该成功调用onConfirm expect(defaultProps.onConfirm).toHaveBeenCalledWith('商家缺货') }) it('应该调用onConfirm当确认按钮被点击时', () => { const { getByTestId } = render() const reasonOption = getByTestId('cancel-reason-我不想买了') fireEvent.click(reasonOption) const confirmButton = getByTestId('confirm-cancel-button') fireEvent.click(confirmButton) expect(defaultProps.onConfirm).toHaveBeenCalledWith('我不想买了') }) it('应该调用onOpenChange当取消按钮被点击时', () => { const { getByText } = render() const cancelButton = getByText('取消') fireEvent.click(cancelButton) expect(defaultProps.onOpenChange).toHaveBeenCalledWith(false) }) it('应该显示错误当确认空原因时', () => { const { getByTestId, getByText } = render() const confirmButton = getByTestId('confirm-cancel-button') fireEvent.click(confirmButton) // 使用更精确的查询方式查找错误消息 const errorMessage = getByText('请输入取消原因') expect(errorMessage).toBeTruthy() expect(defaultProps.onConfirm).not.toHaveBeenCalled() }) it('应该显示错误当原因超过200字符时', () => { const { getByPlaceholderText, getByTestId, getByText } = render( ) const input = getByPlaceholderText('请输入其他取消原因...') fireEvent.input(input, { target: { value: 'a'.repeat(201) } }) const confirmButton = getByTestId('confirm-cancel-button') fireEvent.click(confirmButton) const errorMessage = getByText('取消原因不能超过200个字符') expect(errorMessage).toBeTruthy() expect(defaultProps.onConfirm).not.toHaveBeenCalled() }) it('应该处理自定义原因输入', () => { const { getByPlaceholderText, getByTestId } = render( ) const input = getByPlaceholderText('请输入其他取消原因...') fireEvent.input(input, { target: { value: '自定义取消原因' } }) const confirmButton = getByTestId('confirm-cancel-button') fireEvent.click(confirmButton) expect(defaultProps.onConfirm).toHaveBeenCalledWith('自定义取消原因') }) it('应该显示加载状态当loading为true时', () => { const { getByTestId } = render( ) const confirmButton = getByTestId('confirm-cancel-button') expect(confirmButton).toHaveTextContent('提交中...') }) it('应该禁用按钮当loading为true时', () => { const { getByText, getByTestId } = render( ) const cancelButton = getByText('取消') const confirmButton = getByTestId('confirm-cancel-button') // 检查按钮是否被禁用 expect(cancelButton).toBeTruthy() expect(confirmButton).toBeTruthy() }) it('应该重置状态当对话框关闭时', () => { const { getByTestId, getByText, rerender } = render( ) const reasonOption = getByTestId('cancel-reason-我不想买了') fireEvent.click(reasonOption) // 重新渲染关闭的对话框 rerender() // 重新渲染打开的对话框 rerender() // 检查状态是否重置 - 直接点击确认按钮应该显示错误 const confirmButton = getByTestId('confirm-cancel-button') fireEvent.click(confirmButton) const errorMessage = getByText('请输入取消原因') expect(errorMessage).toBeTruthy() }) })