import { render, fireEvent, waitFor } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { mockShowModal, mockShowToast, mockGetNetworkType } from '~/__mocks__/taroMock'
import OrderButtonBar from '@/components/order/OrderButtonBar'
// Mock specific React Query hooks
jest.mock('@tanstack/react-query', () => {
const originalModule = jest.requireActual('@tanstack/react-query')
return {
...originalModule,
useMutation: jest.fn(() => ({
mutate: jest.fn(),
mutateAsync: jest.fn(),
isLoading: false,
isError: false,
isSuccess: false,
error: null,
data: null
})),
useQueryClient: jest.fn(() => ({
invalidateQueries: jest.fn()
}))
}
})
// Mock API client
jest.mock('@/api', () => ({
orderClient: {
cancelOrder: {
$post: jest.fn()
}
}
}))
const mockOrder = {
id: 1,
orderNo: 'ORDER001',
payState: 0, // 未支付
state: 0, // 未发货
amount: 100,
payAmount: 100,
freightAmount: 0,
discountAmount: 0,
goodsDetail: JSON.stringify([
{ id: 1, name: '商品1', price: 50, num: 2, image: '', spec: '默认规格' }
]),
recevierName: '张三',
receiverMobile: '13800138000',
address: '北京市朝阳区',
createdAt: '2025-01-01T00:00:00Z'
}
const createTestQueryClient = () => new QueryClient({
defaultOptions: {
queries: { retry: false },
mutations: { retry: false }
}
})
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
{children}
)
describe('OrderButtonBar', () => {
beforeEach(() => {
jest.clearAllMocks()
mockGetNetworkType.mockResolvedValue({ networkType: 'wifi' })
})
it('should render cancel button for unpaid order', () => {
const { getByText } = render(
)
expect(getByText('取消订单')).toBeTruthy()
expect(getByText('去支付')).toBeTruthy()
expect(getByText('查看详情')).toBeTruthy()
})
it('should show cancel reason dialog when cancel button is clicked', async () => {
mockGetNetworkType.mockResolvedValue({ networkType: 'wifi' })
mockShowModal.mockResolvedValue({ confirm: true, content: '测试取消原因' })
const { getByText } = render(
)
fireEvent.click(getByText('取消订单'))
await waitFor(() => {
expect(mockShowModal).toHaveBeenCalledWith({
title: '取消订单',
content: '请填写取消原因:',
editable: true,
placeholderText: '请输入取消原因(必填)'
})
})
})
it('should call API when cancel order is confirmed', async () => {
const mockApiCall = require('@/api').orderClient.cancelOrder.$post as jest.Mock
mockGetNetworkType.mockResolvedValue({ networkType: 'wifi' })
mockShowModal
.mockResolvedValueOnce({ confirm: true, content: '测试取消原因' }) // 原因输入
.mockResolvedValueOnce({ confirm: true }) // 确认取消
mockApiCall.mockResolvedValue({ status: 200, json: () => Promise.resolve({ success: true, message: '取消成功' }) })
const { getByText } = render(
)
fireEvent.click(getByText('取消订单'))
await waitFor(() => {
expect(mockApiCall).toHaveBeenCalledWith({
json: {
orderId: 1,
reason: '测试取消原因'
}
})
})
})
it('should show error when cancel reason is empty', async () => {
mockGetNetworkType.mockResolvedValue({ networkType: 'wifi' })
mockShowModal.mockResolvedValue({ confirm: true, content: '' })
const { getByText } = render(
)
fireEvent.click(getByText('取消订单'))
await waitFor(() => {
expect(mockShowToast).toHaveBeenCalledWith({
title: '请填写取消原因',
icon: 'error',
duration: 2000
})
})
})
it('should handle network error gracefully', async () => {
const mockApiCall = require('@/api').orderClient.cancelOrder.$post as jest.Mock
mockGetNetworkType.mockResolvedValue({ networkType: 'wifi' })
mockShowModal
.mockResolvedValueOnce({ confirm: true, content: '测试取消原因' })
.mockResolvedValueOnce({ confirm: true })
mockApiCall.mockRejectedValue(new Error('网络连接失败'))
const { getByText } = render(
)
fireEvent.click(getByText('取消订单'))
await waitFor(() => {
expect(mockShowToast).toHaveBeenCalledWith({
title: '网络连接失败,请检查网络后重试',
icon: 'error',
duration: 3000
})
})
})
it('should disable cancel button during mutation', async () => {
const mockApiCall = require('@/api').orderClient.cancelOrder.$post as jest.Mock
mockGetNetworkType.mockResolvedValue({ networkType: 'wifi' })
mockShowModal
.mockResolvedValueOnce({ confirm: true, content: '测试取消原因' })
.mockResolvedValueOnce({ confirm: true })
// 模拟API调用延迟
mockApiCall.mockImplementation(() => new Promise(resolve => {
setTimeout(() => resolve({ status: 200, json: () => Promise.resolve({ success: true }) }), 100)
}))
const { getByText } = render(
)
fireEvent.click(getByText('取消订单'))
await waitFor(() => {
expect(getByText('取消中...')).toBeTruthy()
})
})
it('should not show cancel button for shipped order', () => {
const shippedOrder = { ...mockOrder, state: 1 } // 已发货
const { queryByText } = render(
)
expect(queryByText('取消订单')).toBeNull()
expect(queryByText('确认收货')).toBeTruthy()
})
it('should use external cancel handler when provided', async () => {
const mockOnCancelOrder = jest.fn()
mockGetNetworkType.mockResolvedValue({ networkType: 'wifi' })
const { getByText } = render(
)
fireEvent.click(getByText('取消订单'))
await waitFor(() => {
expect(mockOnCancelOrder).toHaveBeenCalled()
})
})
})