- Add global search in top bar for navigating to dashboard pages - Add cancellation policy settings (window hours, late fee, deposit refund) - Display booking policies on customer confirmation page - Filter API tokens by sandbox/live mode - Widen settings layout and full-width site builder - Add help documentation search with OpenAI integration - Add blocked time ranges API for calendar visualization - Update business hours settings with holiday management 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
709 lines
22 KiB
TypeScript
709 lines
22 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { renderHook, waitFor, act } from '@testing-library/react';
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
import React from 'react';
|
|
import {
|
|
staffEmailKeys,
|
|
useStaffEmailFolders,
|
|
useCreateStaffEmailFolder,
|
|
useUpdateStaffEmailFolder,
|
|
useDeleteStaffEmailFolder,
|
|
useStaffEmail,
|
|
useStaffEmailThread,
|
|
useStaffEmailLabels,
|
|
useCreateLabel,
|
|
useUpdateLabel,
|
|
useDeleteLabel,
|
|
useAddLabelToEmail,
|
|
useRemoveLabelFromEmail,
|
|
useCreateDraft,
|
|
useUpdateDraft,
|
|
useDeleteDraft,
|
|
useSendEmail,
|
|
useReplyToEmail,
|
|
useForwardEmail,
|
|
useMarkAsRead,
|
|
useMarkAsUnread,
|
|
useStarEmail,
|
|
useUnstarEmail,
|
|
useArchiveEmail,
|
|
useTrashEmail,
|
|
useRestoreEmail,
|
|
usePermanentlyDeleteEmail,
|
|
useMoveEmails,
|
|
useBulkEmailAction,
|
|
useContactSearch,
|
|
useUploadAttachment,
|
|
useDeleteAttachment,
|
|
useSyncEmails,
|
|
useFullSyncEmails,
|
|
useUserEmailAddresses,
|
|
} from '../useStaffEmail';
|
|
import * as staffEmailApi from '../../api/staffEmail';
|
|
|
|
vi.mock('../../api/staffEmail');
|
|
|
|
const mockFolder = {
|
|
id: 1,
|
|
owner: 1,
|
|
name: 'Inbox',
|
|
folderType: 'inbox',
|
|
emailCount: 10,
|
|
unreadCount: 3,
|
|
createdAt: '2024-01-01T00:00:00Z',
|
|
updatedAt: '2024-01-01T00:00:00Z',
|
|
};
|
|
|
|
const mockEmail = {
|
|
id: 1,
|
|
folder: 1,
|
|
fromAddress: 'sender@example.com',
|
|
fromName: 'Sender Name',
|
|
toAddresses: [{ email: 'recipient@example.com', name: 'Recipient' }],
|
|
subject: 'Test Email',
|
|
snippet: 'This is a test...',
|
|
status: 'received',
|
|
isRead: false,
|
|
isStarred: false,
|
|
isImportant: false,
|
|
hasAttachments: false,
|
|
attachmentCount: 0,
|
|
threadId: 'thread-1',
|
|
emailDate: '2024-01-01T12:00:00Z',
|
|
createdAt: '2024-01-01T12:00:00Z',
|
|
labels: [],
|
|
owner: 1,
|
|
emailAddress: 1,
|
|
messageId: 'msg-1',
|
|
inReplyTo: null,
|
|
references: '',
|
|
ccAddresses: [],
|
|
bccAddresses: [],
|
|
bodyText: 'This is a test email body.',
|
|
bodyHtml: '<p>This is a test email body.</p>',
|
|
isAnswered: false,
|
|
isPermanentlyDeleted: false,
|
|
deletedAt: null,
|
|
attachments: [],
|
|
updatedAt: '2024-01-01T12:00:00Z',
|
|
};
|
|
|
|
const mockLabel = {
|
|
id: 1,
|
|
owner: 1,
|
|
name: 'Important',
|
|
color: '#ef4444',
|
|
createdAt: '2024-01-01T00:00:00Z',
|
|
};
|
|
|
|
const mockContact = {
|
|
id: 1,
|
|
owner: 1,
|
|
email: 'contact@example.com',
|
|
name: 'Contact Name',
|
|
useCount: 5,
|
|
lastUsedAt: '2024-01-01T00:00:00Z',
|
|
};
|
|
|
|
const mockAttachment = {
|
|
id: 1,
|
|
filename: 'document.pdf',
|
|
contentType: 'application/pdf',
|
|
size: 1024,
|
|
url: 'https://example.com/document.pdf',
|
|
createdAt: '2024-01-01T00:00:00Z',
|
|
};
|
|
|
|
const mockUserEmailAddress = {
|
|
id: 1,
|
|
email_address: 'user@example.com',
|
|
display_name: 'User',
|
|
color: '#3b82f6',
|
|
is_default: true,
|
|
last_check_at: '2024-01-01T00:00:00Z',
|
|
emails_processed_count: 100,
|
|
};
|
|
|
|
const createWrapper = () => {
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: { retry: false },
|
|
mutations: { retry: false },
|
|
},
|
|
});
|
|
return function Wrapper({ children }: { children: React.ReactNode }) {
|
|
return React.createElement(QueryClientProvider, { client: queryClient }, children);
|
|
};
|
|
};
|
|
|
|
describe('useStaffEmail hooks', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe('staffEmailKeys', () => {
|
|
it('generates correct query keys', () => {
|
|
expect(staffEmailKeys.all).toEqual(['staffEmail']);
|
|
expect(staffEmailKeys.folders()).toEqual(['staffEmail', 'folders']);
|
|
expect(staffEmailKeys.emails()).toEqual(['staffEmail', 'emails']);
|
|
expect(staffEmailKeys.emailDetail(1)).toEqual(['staffEmail', 'emails', 'detail', 1]);
|
|
expect(staffEmailKeys.emailThread('thread-1')).toEqual(['staffEmail', 'emails', 'thread', 'thread-1']);
|
|
expect(staffEmailKeys.labels()).toEqual(['staffEmail', 'labels']);
|
|
expect(staffEmailKeys.contacts('test')).toEqual(['staffEmail', 'contacts', 'test']);
|
|
expect(staffEmailKeys.userEmailAddresses()).toEqual(['staffEmail', 'userEmailAddresses']);
|
|
});
|
|
|
|
it('generates email list key with filters', () => {
|
|
const filters = { folderId: 1, emailAddressId: 2, search: 'test' };
|
|
const key = staffEmailKeys.emailList(filters);
|
|
expect(key).toContain('staffEmail');
|
|
expect(key).toContain('emails');
|
|
expect(key).toContain('list');
|
|
});
|
|
});
|
|
|
|
describe('useStaffEmailFolders', () => {
|
|
it('fetches email folders', async () => {
|
|
vi.mocked(staffEmailApi.getFolders).mockResolvedValueOnce([mockFolder]);
|
|
|
|
const { result } = renderHook(() => useStaffEmailFolders(), { wrapper: createWrapper() });
|
|
|
|
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
|
|
|
expect(staffEmailApi.getFolders).toHaveBeenCalled();
|
|
expect(result.current.data).toEqual([mockFolder]);
|
|
});
|
|
|
|
it('handles error when fetching folders', async () => {
|
|
vi.mocked(staffEmailApi.getFolders).mockRejectedValueOnce(new Error('Failed to fetch folders'));
|
|
|
|
const { result } = renderHook(() => useStaffEmailFolders(), { wrapper: createWrapper() });
|
|
|
|
await waitFor(() => expect(result.current.isError).toBe(true));
|
|
});
|
|
});
|
|
|
|
describe('useCreateStaffEmailFolder', () => {
|
|
it('creates a new folder', async () => {
|
|
const newFolder = { ...mockFolder, id: 2, name: 'Custom Folder' };
|
|
vi.mocked(staffEmailApi.createFolder).mockResolvedValueOnce(newFolder);
|
|
|
|
const { result } = renderHook(() => useCreateStaffEmailFolder(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync('Custom Folder');
|
|
});
|
|
|
|
expect(staffEmailApi.createFolder).toHaveBeenCalledWith('Custom Folder');
|
|
});
|
|
});
|
|
|
|
describe('useUpdateStaffEmailFolder', () => {
|
|
it('updates a folder name', async () => {
|
|
const updatedFolder = { ...mockFolder, name: 'Updated Name' };
|
|
vi.mocked(staffEmailApi.updateFolder).mockResolvedValueOnce(updatedFolder);
|
|
|
|
const { result } = renderHook(() => useUpdateStaffEmailFolder(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync({ id: 1, name: 'Updated Name' });
|
|
});
|
|
|
|
expect(staffEmailApi.updateFolder).toHaveBeenCalledWith(1, 'Updated Name');
|
|
});
|
|
});
|
|
|
|
describe('useDeleteStaffEmailFolder', () => {
|
|
it('deletes a folder', async () => {
|
|
vi.mocked(staffEmailApi.deleteFolder).mockResolvedValueOnce(undefined);
|
|
|
|
const { result } = renderHook(() => useDeleteStaffEmailFolder(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(1);
|
|
});
|
|
|
|
expect(staffEmailApi.deleteFolder).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|
|
|
|
describe('useStaffEmail', () => {
|
|
it('fetches a single email by id', async () => {
|
|
vi.mocked(staffEmailApi.getEmail).mockResolvedValueOnce(mockEmail);
|
|
|
|
const { result } = renderHook(() => useStaffEmail(1), { wrapper: createWrapper() });
|
|
|
|
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
|
|
|
expect(staffEmailApi.getEmail).toHaveBeenCalledWith(1);
|
|
expect(result.current.data).toEqual(mockEmail);
|
|
});
|
|
|
|
it('does not fetch when id is undefined', () => {
|
|
const { result } = renderHook(() => useStaffEmail(undefined), { wrapper: createWrapper() });
|
|
|
|
expect(result.current.fetchStatus).toBe('idle');
|
|
expect(staffEmailApi.getEmail).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('useStaffEmailThread', () => {
|
|
it('fetches email thread', async () => {
|
|
vi.mocked(staffEmailApi.getEmailThread).mockResolvedValueOnce([mockEmail]);
|
|
|
|
const { result } = renderHook(() => useStaffEmailThread('thread-1'), { wrapper: createWrapper() });
|
|
|
|
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
|
|
|
expect(staffEmailApi.getEmailThread).toHaveBeenCalledWith('thread-1');
|
|
expect(result.current.data).toEqual([mockEmail]);
|
|
});
|
|
|
|
it('does not fetch when threadId is undefined', () => {
|
|
const { result } = renderHook(() => useStaffEmailThread(undefined), { wrapper: createWrapper() });
|
|
|
|
expect(result.current.fetchStatus).toBe('idle');
|
|
expect(staffEmailApi.getEmailThread).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('useStaffEmailLabels', () => {
|
|
it('fetches email labels', async () => {
|
|
vi.mocked(staffEmailApi.getLabels).mockResolvedValueOnce([mockLabel]);
|
|
|
|
const { result } = renderHook(() => useStaffEmailLabels(), { wrapper: createWrapper() });
|
|
|
|
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
|
|
|
expect(staffEmailApi.getLabels).toHaveBeenCalled();
|
|
expect(result.current.data).toEqual([mockLabel]);
|
|
});
|
|
});
|
|
|
|
describe('useCreateLabel', () => {
|
|
it('creates a new label', async () => {
|
|
const newLabel = { ...mockLabel, id: 2, name: 'Work', color: '#10b981' };
|
|
vi.mocked(staffEmailApi.createLabel).mockResolvedValueOnce(newLabel);
|
|
|
|
const { result } = renderHook(() => useCreateLabel(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync({ name: 'Work', color: '#10b981' });
|
|
});
|
|
|
|
expect(staffEmailApi.createLabel).toHaveBeenCalledWith('Work', '#10b981');
|
|
});
|
|
});
|
|
|
|
describe('useUpdateLabel', () => {
|
|
it('updates a label', async () => {
|
|
const updatedLabel = { ...mockLabel, name: 'Updated Label' };
|
|
vi.mocked(staffEmailApi.updateLabel).mockResolvedValueOnce(updatedLabel);
|
|
|
|
const { result } = renderHook(() => useUpdateLabel(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync({ id: 1, data: { name: 'Updated Label' } });
|
|
});
|
|
|
|
expect(staffEmailApi.updateLabel).toHaveBeenCalledWith(1, { name: 'Updated Label' });
|
|
});
|
|
});
|
|
|
|
describe('useDeleteLabel', () => {
|
|
it('deletes a label', async () => {
|
|
vi.mocked(staffEmailApi.deleteLabel).mockResolvedValueOnce(undefined);
|
|
|
|
const { result } = renderHook(() => useDeleteLabel(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(1);
|
|
});
|
|
|
|
expect(staffEmailApi.deleteLabel).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|
|
|
|
describe('useAddLabelToEmail', () => {
|
|
it('adds label to email', async () => {
|
|
vi.mocked(staffEmailApi.addLabelToEmail).mockResolvedValueOnce(undefined);
|
|
|
|
const { result } = renderHook(() => useAddLabelToEmail(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync({ emailId: 1, labelId: 2 });
|
|
});
|
|
|
|
expect(staffEmailApi.addLabelToEmail).toHaveBeenCalledWith(1, 2);
|
|
});
|
|
});
|
|
|
|
describe('useRemoveLabelFromEmail', () => {
|
|
it('removes label from email', async () => {
|
|
vi.mocked(staffEmailApi.removeLabelFromEmail).mockResolvedValueOnce(undefined);
|
|
|
|
const { result } = renderHook(() => useRemoveLabelFromEmail(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync({ emailId: 1, labelId: 2 });
|
|
});
|
|
|
|
expect(staffEmailApi.removeLabelFromEmail).toHaveBeenCalledWith(1, 2);
|
|
});
|
|
});
|
|
|
|
describe('useCreateDraft', () => {
|
|
it('creates a draft email', async () => {
|
|
vi.mocked(staffEmailApi.createDraft).mockResolvedValueOnce(mockEmail);
|
|
|
|
const draftData = {
|
|
emailAddressId: 1,
|
|
toAddresses: ['recipient@example.com'],
|
|
subject: 'Test Draft',
|
|
bodyText: 'Draft body',
|
|
};
|
|
|
|
const { result } = renderHook(() => useCreateDraft(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(draftData);
|
|
});
|
|
|
|
expect(staffEmailApi.createDraft).toHaveBeenCalledWith(draftData);
|
|
});
|
|
});
|
|
|
|
describe('useUpdateDraft', () => {
|
|
it('updates a draft email', async () => {
|
|
vi.mocked(staffEmailApi.updateDraft).mockResolvedValueOnce(mockEmail);
|
|
|
|
const { result } = renderHook(() => useUpdateDraft(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync({ id: 1, data: { subject: 'Updated Subject' } });
|
|
});
|
|
|
|
expect(staffEmailApi.updateDraft).toHaveBeenCalledWith(1, { subject: 'Updated Subject' });
|
|
});
|
|
});
|
|
|
|
describe('useDeleteDraft', () => {
|
|
it('deletes a draft', async () => {
|
|
vi.mocked(staffEmailApi.deleteDraft).mockResolvedValueOnce(undefined);
|
|
|
|
const { result } = renderHook(() => useDeleteDraft(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(1);
|
|
});
|
|
|
|
expect(staffEmailApi.deleteDraft).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|
|
|
|
describe('useSendEmail', () => {
|
|
it('sends an email', async () => {
|
|
vi.mocked(staffEmailApi.sendEmail).mockResolvedValueOnce(mockEmail);
|
|
|
|
const { result } = renderHook(() => useSendEmail(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(1);
|
|
});
|
|
|
|
expect(staffEmailApi.sendEmail).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|
|
|
|
describe('useReplyToEmail', () => {
|
|
it('replies to an email', async () => {
|
|
vi.mocked(staffEmailApi.replyToEmail).mockResolvedValueOnce(mockEmail);
|
|
|
|
const replyData = {
|
|
bodyText: 'Reply body',
|
|
bodyHtml: '<p>Reply body</p>',
|
|
replyAll: false,
|
|
};
|
|
|
|
const { result } = renderHook(() => useReplyToEmail(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync({ id: 1, data: replyData });
|
|
});
|
|
|
|
expect(staffEmailApi.replyToEmail).toHaveBeenCalledWith(1, replyData);
|
|
});
|
|
});
|
|
|
|
describe('useForwardEmail', () => {
|
|
it('forwards an email', async () => {
|
|
vi.mocked(staffEmailApi.forwardEmail).mockResolvedValueOnce(mockEmail);
|
|
|
|
const forwardData = {
|
|
toAddresses: ['forward@example.com'],
|
|
bodyText: 'Forwarding this email',
|
|
};
|
|
|
|
const { result } = renderHook(() => useForwardEmail(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync({ id: 1, data: forwardData });
|
|
});
|
|
|
|
expect(staffEmailApi.forwardEmail).toHaveBeenCalledWith(1, forwardData);
|
|
});
|
|
});
|
|
|
|
describe('useMarkAsRead', () => {
|
|
it('marks email as read', async () => {
|
|
vi.mocked(staffEmailApi.markAsRead).mockResolvedValueOnce(undefined);
|
|
|
|
const { result } = renderHook(() => useMarkAsRead(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(1);
|
|
});
|
|
|
|
expect(staffEmailApi.markAsRead).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|
|
|
|
describe('useMarkAsUnread', () => {
|
|
it('marks email as unread', async () => {
|
|
vi.mocked(staffEmailApi.markAsUnread).mockResolvedValueOnce(undefined);
|
|
|
|
const { result } = renderHook(() => useMarkAsUnread(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(1);
|
|
});
|
|
|
|
expect(staffEmailApi.markAsUnread).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|
|
|
|
describe('useStarEmail', () => {
|
|
it('stars an email', async () => {
|
|
vi.mocked(staffEmailApi.starEmail).mockResolvedValueOnce(undefined);
|
|
|
|
const { result } = renderHook(() => useStarEmail(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(1);
|
|
});
|
|
|
|
expect(staffEmailApi.starEmail).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|
|
|
|
describe('useUnstarEmail', () => {
|
|
it('unstars an email', async () => {
|
|
vi.mocked(staffEmailApi.unstarEmail).mockResolvedValueOnce(undefined);
|
|
|
|
const { result } = renderHook(() => useUnstarEmail(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(1);
|
|
});
|
|
|
|
expect(staffEmailApi.unstarEmail).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|
|
|
|
describe('useArchiveEmail', () => {
|
|
it('archives an email', async () => {
|
|
vi.mocked(staffEmailApi.archiveEmail).mockResolvedValueOnce(undefined);
|
|
|
|
const { result } = renderHook(() => useArchiveEmail(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(1);
|
|
});
|
|
|
|
expect(staffEmailApi.archiveEmail).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|
|
|
|
describe('useTrashEmail', () => {
|
|
it('moves email to trash', async () => {
|
|
vi.mocked(staffEmailApi.trashEmail).mockResolvedValueOnce(undefined);
|
|
|
|
const { result } = renderHook(() => useTrashEmail(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(1);
|
|
});
|
|
|
|
expect(staffEmailApi.trashEmail).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|
|
|
|
describe('useRestoreEmail', () => {
|
|
it('restores an email from trash', async () => {
|
|
vi.mocked(staffEmailApi.restoreEmail).mockResolvedValueOnce(undefined);
|
|
|
|
const { result } = renderHook(() => useRestoreEmail(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(1);
|
|
});
|
|
|
|
expect(staffEmailApi.restoreEmail).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|
|
|
|
describe('usePermanentlyDeleteEmail', () => {
|
|
it('permanently deletes an email', async () => {
|
|
vi.mocked(staffEmailApi.permanentlyDeleteEmail).mockResolvedValueOnce(undefined);
|
|
|
|
const { result } = renderHook(() => usePermanentlyDeleteEmail(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(1);
|
|
});
|
|
|
|
expect(staffEmailApi.permanentlyDeleteEmail).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|
|
|
|
describe('useMoveEmails', () => {
|
|
it('moves emails to a folder', async () => {
|
|
vi.mocked(staffEmailApi.moveEmails).mockResolvedValueOnce(undefined);
|
|
|
|
const moveData = { emailIds: [1, 2, 3], folderId: 2 };
|
|
|
|
const { result } = renderHook(() => useMoveEmails(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(moveData);
|
|
});
|
|
|
|
expect(staffEmailApi.moveEmails).toHaveBeenCalledWith(moveData);
|
|
});
|
|
});
|
|
|
|
describe('useBulkEmailAction', () => {
|
|
it('performs bulk action on emails', async () => {
|
|
vi.mocked(staffEmailApi.bulkAction).mockResolvedValueOnce(undefined);
|
|
|
|
const bulkData = { emailIds: [1, 2, 3], action: 'mark_read' as const };
|
|
|
|
const { result } = renderHook(() => useBulkEmailAction(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(bulkData);
|
|
});
|
|
|
|
expect(staffEmailApi.bulkAction).toHaveBeenCalledWith(bulkData);
|
|
});
|
|
});
|
|
|
|
describe('useContactSearch', () => {
|
|
it('searches contacts with query', async () => {
|
|
vi.mocked(staffEmailApi.searchContacts).mockResolvedValueOnce([mockContact]);
|
|
|
|
const { result } = renderHook(() => useContactSearch('test'), { wrapper: createWrapper() });
|
|
|
|
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
|
|
|
expect(staffEmailApi.searchContacts).toHaveBeenCalledWith('test');
|
|
expect(result.current.data).toEqual([mockContact]);
|
|
});
|
|
|
|
it('does not search with query less than 2 characters', () => {
|
|
const { result } = renderHook(() => useContactSearch('t'), { wrapper: createWrapper() });
|
|
|
|
expect(result.current.fetchStatus).toBe('idle');
|
|
expect(staffEmailApi.searchContacts).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('useUploadAttachment', () => {
|
|
it('uploads an attachment', async () => {
|
|
vi.mocked(staffEmailApi.uploadAttachment).mockResolvedValueOnce(mockAttachment);
|
|
|
|
const file = new File(['test content'], 'test.pdf', { type: 'application/pdf' });
|
|
|
|
const { result } = renderHook(() => useUploadAttachment(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync({ file, emailId: 1 });
|
|
});
|
|
|
|
expect(staffEmailApi.uploadAttachment).toHaveBeenCalledWith(file, 1);
|
|
});
|
|
|
|
it('uploads attachment without email id', async () => {
|
|
vi.mocked(staffEmailApi.uploadAttachment).mockResolvedValueOnce(mockAttachment);
|
|
|
|
const file = new File(['test content'], 'test.pdf', { type: 'application/pdf' });
|
|
|
|
const { result } = renderHook(() => useUploadAttachment(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync({ file });
|
|
});
|
|
|
|
expect(staffEmailApi.uploadAttachment).toHaveBeenCalledWith(file, undefined);
|
|
});
|
|
});
|
|
|
|
describe('useDeleteAttachment', () => {
|
|
it('deletes an attachment', async () => {
|
|
vi.mocked(staffEmailApi.deleteAttachment).mockResolvedValueOnce(undefined);
|
|
|
|
const { result } = renderHook(() => useDeleteAttachment(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync(1);
|
|
});
|
|
|
|
expect(staffEmailApi.deleteAttachment).toHaveBeenCalledWith(1);
|
|
});
|
|
});
|
|
|
|
describe('useSyncEmails', () => {
|
|
it('syncs emails', async () => {
|
|
vi.mocked(staffEmailApi.syncEmails).mockResolvedValueOnce({ success: true, message: 'Synced' });
|
|
|
|
const { result } = renderHook(() => useSyncEmails(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync();
|
|
});
|
|
|
|
expect(staffEmailApi.syncEmails).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('useFullSyncEmails', () => {
|
|
it('performs full email sync', async () => {
|
|
vi.mocked(staffEmailApi.fullSyncEmails).mockResolvedValueOnce({
|
|
status: 'started',
|
|
tasks: [{ email_address: 'user@example.com', task_id: 'task-1' }],
|
|
});
|
|
|
|
const { result } = renderHook(() => useFullSyncEmails(), { wrapper: createWrapper() });
|
|
|
|
await act(async () => {
|
|
await result.current.mutateAsync();
|
|
});
|
|
|
|
expect(staffEmailApi.fullSyncEmails).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('useUserEmailAddresses', () => {
|
|
it('fetches user email addresses', async () => {
|
|
vi.mocked(staffEmailApi.getUserEmailAddresses).mockResolvedValueOnce([mockUserEmailAddress]);
|
|
|
|
const { result } = renderHook(() => useUserEmailAddresses(), { wrapper: createWrapper() });
|
|
|
|
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
|
|
|
expect(staffEmailApi.getUserEmailAddresses).toHaveBeenCalled();
|
|
expect(result.current.data).toEqual([mockUserEmailAddress]);
|
|
});
|
|
});
|
|
});
|