Updated the Vitest configuration to increase test and hook timeouts to 20 seconds, addressing flaky tests under pre-push load. Additionally, improved test cleanup procedures across multiple test files by ensuring mocks are cleared after each test, enhancing test reliability and maintainability.
183 lines
5.8 KiB
TypeScript
183 lines
5.8 KiB
TypeScript
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||
|
||
import EventDiscountsPanel from '@/features/events/detail/EventDiscountsPanel'
|
||
|
||
const listCodes = vi.fn()
|
||
const listRedemptions = vi.fn()
|
||
const getReport = vi.fn()
|
||
const getBootstrap = vi.fn()
|
||
const bulkCreate = vi.fn()
|
||
const setActive = vi.fn()
|
||
const removeCode = vi.fn()
|
||
const addToast = vi.fn()
|
||
const showAlert = vi.fn((_message: string, onConfirm?: () => unknown) => {
|
||
void onConfirm?.()
|
||
})
|
||
|
||
vi.mock('@/lib/toast', () => ({ addToast: (...args: unknown[]) => addToast(...args) }))
|
||
vi.mock('@/hooks/useAlertModal', () => ({ default: () => ({ showAlert }) }))
|
||
vi.mock('@/services/discountCodes', () => ({
|
||
LIST_DISCOUNT_CODES: (...args: unknown[]) => listCodes(...args),
|
||
LIST_DISCOUNT_REDEMPTIONS: (...args: unknown[]) => listRedemptions(...args),
|
||
GET_DISCOUNT_REPORT: (...args: unknown[]) => getReport(...args),
|
||
GET_DISCOUNT_MANAGEMENT_BOOTSTRAP: (...args: unknown[]) => getBootstrap(...args),
|
||
BULK_CREATE_DISCOUNT_CODES: (...args: unknown[]) => bulkCreate(...args),
|
||
SET_DISCOUNT_CODE_ACTIVE: (...args: unknown[]) => setActive(...args),
|
||
DELETE_DISCOUNT_CODE: (...args: unknown[]) => removeCode(...args),
|
||
}))
|
||
vi.mock('@/components/formElements/Button', () => ({
|
||
default: ({
|
||
children,
|
||
isLoading,
|
||
fullWidth: _fullWidth,
|
||
...props
|
||
}: React.ButtonHTMLAttributes<HTMLButtonElement> & { isLoading?: boolean; fullWidth?: boolean }) => (
|
||
<button
|
||
{...props}
|
||
disabled={props.disabled || isLoading}
|
||
>
|
||
{children}
|
||
</button>
|
||
),
|
||
}))
|
||
|
||
const usedCode = {
|
||
id: 'code-used',
|
||
eventId: 'event-1',
|
||
code: 'WELCOME20',
|
||
type: 'percent' as const,
|
||
value: 20,
|
||
bearer: 'organizer' as const,
|
||
maxUses: null,
|
||
maxUsesPerUser: null,
|
||
validFrom: null,
|
||
validUntil: null,
|
||
generationBatchId: 'batch-1',
|
||
isActive: true,
|
||
redeemedCount: 2,
|
||
totalDiscountAmount: 140000,
|
||
createdAt: '2030-01-01T09:00:00.000Z',
|
||
}
|
||
|
||
const freshCode = {
|
||
...usedCode,
|
||
id: 'code-fresh',
|
||
code: 'SAVE50K',
|
||
type: 'fixed' as const,
|
||
value: 50000,
|
||
redeemedCount: 0,
|
||
totalDiscountAmount: 0,
|
||
}
|
||
|
||
describe('EventDiscountsPanel', () => {
|
||
afterEach(() => {
|
||
cleanup()
|
||
vi.clearAllMocks()
|
||
})
|
||
|
||
beforeEach(() => {
|
||
vi.clearAllMocks()
|
||
showAlert.mockImplementation((_message: string, onConfirm?: () => unknown) => {
|
||
void onConfirm?.()
|
||
})
|
||
listCodes.mockResolvedValue({ ok: true, data: { items: [usedCode, freshCode], totalItemsCount: 2, totalPages: 1 } })
|
||
listRedemptions.mockResolvedValue({ ok: true, data: { items: [], totalItemsCount: 0, totalPages: 0 } })
|
||
getReport.mockResolvedValue({
|
||
ok: true,
|
||
data: { totalCodes: 2, totalRedemptions: 2, totalDiscountAmount: 140000, totalPlatformAbsorbed: 0, totalOrganizerAbsorbed: 140000 },
|
||
})
|
||
getBootstrap.mockResolvedValue({
|
||
ok: true,
|
||
data: {
|
||
codes: { items: [usedCode, freshCode], totalItemsCount: 2, totalPages: 1 },
|
||
redemptions: { items: [], totalItemsCount: 0, totalPages: 0 },
|
||
report: {
|
||
totalCodes: 2,
|
||
totalRedemptions: 2,
|
||
totalDiscountAmount: 140000,
|
||
totalPlatformAbsorbed: 0,
|
||
totalOrganizerAbsorbed: 140000,
|
||
},
|
||
},
|
||
})
|
||
bulkCreate.mockResolvedValue({ ok: true, data: { batchId: 'b', count: 2, codes: ['AAAA1111', 'BBBB2222'], items: [] } })
|
||
removeCode.mockResolvedValue({ ok: true, data: undefined })
|
||
})
|
||
|
||
it('renders a message and skips loading for free events', () => {
|
||
render(
|
||
<EventDiscountsPanel
|
||
isFree
|
||
eventId="event-1"
|
||
/>
|
||
)
|
||
|
||
expect(screen.getByText('این ایونت رایگان است')).toBeInTheDocument()
|
||
expect(getBootstrap).not.toHaveBeenCalled()
|
||
expect(listCodes).not.toHaveBeenCalled()
|
||
})
|
||
|
||
it('lists codes and disables delete for a used code', async () => {
|
||
render(
|
||
<EventDiscountsPanel
|
||
eventId="event-1"
|
||
isFree={false}
|
||
/>
|
||
)
|
||
|
||
expect(await screen.findByText('WELCOME20')).toBeInTheDocument()
|
||
expect(screen.getByText('SAVE50K')).toBeInTheDocument()
|
||
|
||
const deleteButtons = screen.getAllByRole('button', { name: 'حذف' })
|
||
|
||
// First card is the used code — its delete must be disabled.
|
||
expect(deleteButtons[0]).toBeDisabled()
|
||
// Second card is the unused code — deletable.
|
||
expect(deleteButtons[1]).toBeEnabled()
|
||
})
|
||
|
||
it('bulk-creates codes and reveals the generated list', async () => {
|
||
render(
|
||
<EventDiscountsPanel
|
||
eventId="event-1"
|
||
isFree={false}
|
||
/>
|
||
)
|
||
|
||
await screen.findByText('WELCOME20')
|
||
bulkCreate.mockClear()
|
||
|
||
fireEvent.change(screen.getByLabelText('درصد تخفیف (۱ تا ۹۹)'), { target: { value: '25' } })
|
||
fireEvent.change(screen.getByLabelText('تعداد کد یکتا'), { target: { value: '2' } })
|
||
fireEvent.click(screen.getByRole('button', { name: 'ساخت کد تخفیف' }))
|
||
|
||
await waitFor(() => {
|
||
expect(bulkCreate).toHaveBeenCalledWith('event-1', expect.objectContaining({ type: 'percent', value: 25, quantity: 2 }))
|
||
})
|
||
expect(await screen.findByText('AAAA1111')).toBeInTheDocument()
|
||
expect(screen.getByText('BBBB2222')).toBeInTheDocument()
|
||
})
|
||
|
||
it('rejects an out-of-range percent value before calling the API', async () => {
|
||
render(
|
||
<EventDiscountsPanel
|
||
eventId="event-1"
|
||
isFree={false}
|
||
/>
|
||
)
|
||
|
||
await screen.findByText('WELCOME20')
|
||
bulkCreate.mockClear()
|
||
addToast.mockClear()
|
||
|
||
fireEvent.change(screen.getByLabelText('درصد تخفیف (۱ تا ۹۹)'), { target: { value: '150' } })
|
||
fireEvent.click(screen.getByRole('button', { name: 'ساخت کد تخفیف' }))
|
||
|
||
await waitFor(() => {
|
||
expect(addToast).toHaveBeenCalledWith(expect.objectContaining({ color: 'warning' }))
|
||
})
|
||
expect(bulkCreate).not.toHaveBeenCalled()
|
||
})
|
||
})
|