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 & { isLoading?: boolean; fullWidth?: boolean }) => ( ), })) 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( ) expect(screen.getByText('این ایونت رایگان است')).toBeInTheDocument() expect(getBootstrap).not.toHaveBeenCalled() expect(listCodes).not.toHaveBeenCalled() }) it('lists codes and disables delete for a used code', async () => { render( ) 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( ) 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( ) 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() }) })