27a8ad8b30
- Remove duplicate vi.mock(@/api/hooks) in SettingsRoles.test.tsx that overrode forceUpdate logic - SettingsRoles mock: use vi.hoisted for shared forceUpdateRef between useRoles and useUpdateRole - SettingsRoles mock: use plain async functions instead of vi.fn().mockImplementation for mutations
164 lines
6.3 KiB
TypeScript
164 lines
6.3 KiB
TypeScript
import React from 'react';
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
import { MemoryRouter } from 'react-router-dom';
|
|
|
|
vi.mock('@/api/mail', () => ({
|
|
decodeMimeHeader: (s: string) => s,
|
|
fetchAccounts: vi.fn().mockResolvedValue([
|
|
{ id: 'acc1', email: 'test@example.com', display_name: 'Test User', is_shared: false, is_active: true, imap_host: '', imap_port: 993, smtp_host: '', smtp_port: 587 },
|
|
]),
|
|
fetchFolders: vi.fn().mockResolvedValue([
|
|
{ id: 'f1', account_id: 'acc1', name: 'INBOX', parent_id: null, unread_count: 2, total_count: 5, children: [] },
|
|
{ id: 'f2', account_id: 'acc1', name: 'Sent', parent_id: null, unread_count: 0, total_count: 10, children: [] },
|
|
]),
|
|
fetchMails: vi.fn().mockResolvedValue({
|
|
mails: [
|
|
{ id: 'm1', folder_id: 'f1', account_id: 'acc1', from_address: 'sender@example.com', from_name: 'Sender', to_addresses: ['test@example.com'], cc_addresses: [], bcc_addresses: [], subject: 'Test Subject', body_text: 'Hello', body_html: null, sanitized_html: '<p>Hello</p>', date: '2026-01-01T10:00:00Z', is_seen: false, is_flagged: false, is_answered: false, has_attachments: false, labels: [] },
|
|
{ id: 'm2', folder_id: 'f1', account_id: 'acc1', from_address: 'sender2@example.com', from_name: 'Sender 2', to_addresses: ['test@example.com'], cc_addresses: [], bcc_addresses: [], subject: 'Another Subject', body_text: 'World', body_html: null, sanitized_html: '<p>World</p>', date: '2026-01-01T11:00:00Z', is_seen: true, is_flagged: true, is_answered: false, has_attachments: true, labels: [] },
|
|
],
|
|
total: 2,
|
|
page: 1,
|
|
page_size: 25,
|
|
}),
|
|
getMail: vi.fn().mockResolvedValue({
|
|
id: 'm1', folder_id: 'f1', account_id: 'acc1', from_address: 'sender@example.com', from_name: 'Sender', to_addresses: ['test@example.com'], cc_addresses: [], bcc_addresses: [], subject: 'Test Subject', body_text: 'Hello', body_html: '<p>Hello</p>', sanitized_html: '<p>Hello</p>', date: '2026-01-01T10:00:00Z', is_seen: false, is_flagged: false, is_answered: false, has_attachments: false, attachments: [], labels: [],
|
|
}),
|
|
sendMail: vi.fn().mockResolvedValue({ id: 'm3', success: true }),
|
|
replyMail: vi.fn().mockResolvedValue({ id: 'm3', success: true }),
|
|
forwardMail: vi.fn().mockResolvedValue({ id: 'm3', success: true }),
|
|
updateFlags: vi.fn().mockResolvedValue(undefined),
|
|
createEventFromMail: vi.fn().mockResolvedValue({ id: 'e1', success: true }),
|
|
downloadAttachment: vi.fn().mockResolvedValue(new Blob(['test'])),
|
|
fetchSignatures: vi.fn().mockResolvedValue([]),
|
|
searchMails: vi.fn().mockResolvedValue({ mails: [], total: 0, page: 1, page_size: 25 }),
|
|
}));
|
|
|
|
vi.mock('@/components/ui/Toast', () => ({
|
|
useToast: () => ({ success: vi.fn(), error: vi.fn(), info: vi.fn(), warning: vi.fn() }),
|
|
}));
|
|
|
|
import { MailPage } from '@/pages/Mail';
|
|
|
|
function renderWithRouter() {
|
|
return render(<MemoryRouter><MailPage /></MemoryRouter>);
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe('MailPage', () => {
|
|
it('renders mail page after loading', async () => {
|
|
renderWithRouter();
|
|
await waitFor(() => {
|
|
expect(screen.getByTestId('mail-page')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('renders folder tree pane', async () => {
|
|
renderWithRouter();
|
|
await waitFor(() => {
|
|
expect(screen.getByTestId('mail-folder-pane')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('renders mail list pane', async () => {
|
|
renderWithRouter();
|
|
await waitFor(() => {
|
|
expect(screen.getByTestId('mail-list-pane')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('renders mail detail pane', async () => {
|
|
renderWithRouter();
|
|
await waitFor(() => {
|
|
expect(screen.getByTestId('mail-detail-pane')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('renders compose button', async () => {
|
|
renderWithRouter();
|
|
await waitFor(() => {
|
|
expect(screen.getByTestId('compose-btn')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('renders shared mailbox selector', async () => {
|
|
renderWithRouter();
|
|
await waitFor(() => {
|
|
expect(screen.getByTestId('shared-mailbox-selector')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('renders mail search bar', async () => {
|
|
renderWithRouter();
|
|
await waitFor(() => {
|
|
expect(screen.getByTestId('mail-search-bar')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('renders folders after loading', async () => {
|
|
renderWithRouter();
|
|
await waitFor(() => {
|
|
expect(screen.getAllByText('INBOX').length).toBeGreaterThan(0);
|
|
expect(screen.getAllByText('Sent').length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
it('renders mails after loading', async () => {
|
|
renderWithRouter();
|
|
await waitFor(() => {
|
|
expect(screen.getByText('Test Subject')).toBeInTheDocument();
|
|
expect(screen.getByText('Another Subject')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('shows compose modal when compose button is clicked', async () => {
|
|
renderWithRouter();
|
|
await waitFor(() => {
|
|
const btn = screen.getByTestId('compose-btn');
|
|
fireEvent.click(btn);
|
|
expect(screen.getByTestId('compose-modal')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('shows compose toolbar with bold and italic buttons', async () => {
|
|
renderWithRouter();
|
|
await waitFor(() => {
|
|
const btn = screen.getByTestId('compose-btn');
|
|
fireEvent.click(btn);
|
|
expect(screen.getByTestId('compose-toolbar')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('renders mail detail empty state initially', async () => {
|
|
renderWithRouter();
|
|
await waitFor(() => {
|
|
expect(screen.getByTestId('mail-detail-empty')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('renders mail detail when mail is clicked', async () => {
|
|
renderWithRouter();
|
|
await waitFor(() => {
|
|
expect(screen.getByText('Test Subject')).toBeInTheDocument();
|
|
});
|
|
fireEvent.click(screen.getByText('Test Subject'));
|
|
await waitFor(() => {
|
|
expect(screen.getAllByTestId('mail-detail').length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
it('shows reply and forward buttons in mail detail', async () => {
|
|
renderWithRouter();
|
|
await waitFor(() => {
|
|
expect(screen.getByText('Test Subject')).toBeInTheDocument();
|
|
});
|
|
fireEvent.click(screen.getByText('Test Subject'));
|
|
await waitFor(() => {
|
|
expect(screen.getAllByTestId('mail-detail-toolbar').length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
});
|