- Seller KYC via Stripe Identity (start verification, status check, webhook) - Support ticket system (backend handler + frontend form page) - E2E payout flow integration test (sale → payment → balance → payout) - Migrations: seller_kyc columns, support_tickets table - Frontend: SupportPage with SUMI design, lazy loading, routing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
116 lines
4.1 KiB
TypeScript
116 lines
4.1 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { render, screen, waitFor } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
import { SupportPage } from './SupportPage';
|
|
|
|
// Mock dependencies
|
|
vi.mock('react-i18next', () => ({
|
|
useTranslation: () => ({
|
|
t: (key: string, fallback?: string) => fallback || key,
|
|
}),
|
|
}));
|
|
|
|
vi.mock('@/features/auth/hooks/useUser', () => ({
|
|
useUser: () => ({ data: { email: 'test@example.com', username: 'testuser' } }),
|
|
}));
|
|
|
|
vi.mock('@/features/auth/store/authStore', () => ({
|
|
useAuthStore: () => ({ isAuthenticated: true }),
|
|
}));
|
|
|
|
vi.mock('@/components/layout/DashboardLayout', () => ({
|
|
DashboardLayout: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
|
}));
|
|
|
|
const mockPost = vi.fn().mockResolvedValue({ data: { ticket_id: 'ticket-123' } });
|
|
vi.mock('@/services/api/client', () => ({
|
|
apiClient: { post: (...args: unknown[]) => mockPost(...args) },
|
|
}));
|
|
|
|
function renderWithProviders(ui: React.ReactElement) {
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: { queries: { retry: false } },
|
|
});
|
|
return render(
|
|
<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>,
|
|
);
|
|
}
|
|
|
|
describe('SupportPage', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockPost.mockResolvedValue({ data: { ticket_id: 'ticket-123' } });
|
|
});
|
|
|
|
it('should render the support form', () => {
|
|
renderWithProviders(<SupportPage />);
|
|
expect(screen.getByText('Support')).toBeDefined();
|
|
expect(screen.getByLabelText('Email address')).toBeDefined();
|
|
expect(screen.getByLabelText('Subject')).toBeDefined();
|
|
expect(screen.getByLabelText('Message')).toBeDefined();
|
|
});
|
|
|
|
it('should pre-fill email from authenticated user', () => {
|
|
renderWithProviders(<SupportPage />);
|
|
const emailInput = screen.getByLabelText('Email address') as HTMLInputElement;
|
|
expect(emailInput.value).toBe('test@example.com');
|
|
});
|
|
|
|
it('should disable submit when form is incomplete', () => {
|
|
renderWithProviders(<SupportPage />);
|
|
const submitBtn = screen.getByRole('button', { name: /send message/i });
|
|
expect(submitBtn).toBeDisabled();
|
|
});
|
|
|
|
it('should enable submit when form is complete', async () => {
|
|
const user = userEvent.setup();
|
|
renderWithProviders(<SupportPage />);
|
|
|
|
await user.type(screen.getByLabelText('Subject'), 'Payment issue');
|
|
await user.type(screen.getByLabelText('Message'), 'I have a problem with my recent purchase.');
|
|
|
|
const submitBtn = screen.getByRole('button', { name: /send message/i });
|
|
expect(submitBtn).not.toBeDisabled();
|
|
});
|
|
|
|
it('should submit the form and show success message', async () => {
|
|
const user = userEvent.setup();
|
|
renderWithProviders(<SupportPage />);
|
|
|
|
await user.type(screen.getByLabelText('Subject'), 'Payment issue');
|
|
await user.type(screen.getByLabelText('Message'), 'I have a problem with my recent purchase.');
|
|
await user.click(screen.getByRole('button', { name: /send message/i }));
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('Message sent!')).toBeDefined();
|
|
});
|
|
|
|
expect(mockPost).toHaveBeenCalledWith('/support/tickets', expect.objectContaining({
|
|
email: 'test@example.com',
|
|
subject: 'Payment issue',
|
|
category: 'general',
|
|
}));
|
|
});
|
|
|
|
it('should show error on submission failure', async () => {
|
|
mockPost.mockRejectedValueOnce(new Error('Network error'));
|
|
const user = userEvent.setup();
|
|
renderWithProviders(<SupportPage />);
|
|
|
|
await user.type(screen.getByLabelText('Subject'), 'Payment issue');
|
|
await user.type(screen.getByLabelText('Message'), 'I have a problem with my recent purchase.');
|
|
await user.click(screen.getByRole('button', { name: /send message/i }));
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByRole('alert')).toBeDefined();
|
|
});
|
|
});
|
|
|
|
it('should render quick help links', () => {
|
|
renderWithProviders(<SupportPage />);
|
|
expect(screen.getByText('Quick help')).toBeDefined();
|
|
expect(screen.getByText('Payment issues')).toBeDefined();
|
|
expect(screen.getByText('Account settings')).toBeDefined();
|
|
});
|
|
});
|