mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-05 00:14:18 +00:00
fix: close the details panel on the next message and show no delivery time for mail received before it was sent
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import { render, screen, fireEvent, act } from '@testing-library/react';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import type { Email, Mailbox } from '@/lib/jmap/types';
|
||||
|
||||
// mail-7: "Show details" stayed open for every message opened after it.
|
||||
// mail-29: a mail received before it was sent showed a delivery time.
|
||||
|
||||
vi.mock('@/hooks/use-media-query', () => ({
|
||||
useDeviceDetection: () => ({ isMobile: false, isTablet: false, isDesktop: true }),
|
||||
useMediaQuery: () => false,
|
||||
}));
|
||||
vi.mock('@/components/tour/tour-provider', () => ({
|
||||
useTour: () => ({ activeTour: null, startTour: vi.fn(), endTour: vi.fn(), registerStep: vi.fn() }),
|
||||
}));
|
||||
|
||||
class NoopResizeObserver {
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
}
|
||||
vi.stubGlobal('ResizeObserver', NoopResizeObserver);
|
||||
|
||||
const { EmailViewer } = await import('../email-viewer');
|
||||
|
||||
const mailboxes = [
|
||||
{ id: 'inbox', name: 'Inbox', role: 'inbox', parentId: null, myRights: { mayAddItems: true } },
|
||||
] as unknown as Mailbox[];
|
||||
|
||||
const make = (id: string, sentAt: string, receivedAt: string) => ({
|
||||
id, blobId: `b-${id}`, threadId: `t-${id}`, mailboxIds: { inbox: true }, keywords: { $seen: true },
|
||||
from: [{ name: 'Sam', email: 'sam@example.com' }], to: [{ name: 'Bob', email: 'bob@example.com' }],
|
||||
subject: `Message ${id}`, sentAt, receivedAt, preview: 'Hello',
|
||||
hasAttachment: false, size: 100, textBody: [], htmlBody: [], attachments: [], bodyValues: {},
|
||||
}) as unknown as Email;
|
||||
|
||||
const viewer = (email: Email) => (
|
||||
<EmailViewer email={email} mailboxes={mailboxes} selectedMailbox="inbox" currentMailboxRole="inbox" isLoading={false} />
|
||||
);
|
||||
|
||||
describe('message details panel', () => {
|
||||
it('closes when another message opens', () => {
|
||||
const first = make('e1', '2026-11-03T10:00:00Z', '2026-11-03T10:00:05Z');
|
||||
const second = make('e2', '2026-11-03T11:00:00Z', '2026-11-03T11:00:05Z');
|
||||
const { rerender } = render(viewer(first));
|
||||
act(() => { fireEvent.click(screen.getAllByText('show_details')[0]); });
|
||||
expect(screen.queryByText('details.recipients_routing')).not.toBeNull();
|
||||
rerender(viewer(second));
|
||||
expect(screen.queryByText('details.recipients_routing')).toBeNull();
|
||||
// And back on the first one it is closed too: it was opened for that view.
|
||||
rerender(viewer(first));
|
||||
expect(screen.queryByText('details.recipients_routing')).toBeNull();
|
||||
});
|
||||
|
||||
it('shows no delivery time for a mail received before it was sent', () => {
|
||||
render(viewer(make('e3', '2026-11-03T21:40:00Z', '2026-11-03T20:40:00Z')));
|
||||
act(() => { fireEvent.click(screen.getAllByText('show_details')[0]); });
|
||||
expect(screen.queryByText(/details\.delivery_time/i)).toBeNull();
|
||||
expect(document.body.textContent).not.toMatch(/delivery_time/);
|
||||
});
|
||||
|
||||
it('still shows a real delivery time', () => {
|
||||
render(viewer(make('e4', '2026-11-03T20:40:00Z', '2026-11-03T21:40:00Z')));
|
||||
act(() => { fireEvent.click(screen.getAllByText('show_details')[0]); });
|
||||
expect(document.body.textContent).toMatch(/delivery_time/);
|
||||
});
|
||||
});
|
||||
@@ -1670,7 +1670,13 @@ export function EmailViewer({
|
||||
}, [onRescheduleScheduled, t, tComposer, scheduledSendAt, timeFormat]);
|
||||
const resolvedTheme = useThemeStore((state) => state.resolvedTheme);
|
||||
const { startTour } = useTour();
|
||||
const [showFullHeaders, setShowFullHeaders] = useState(false);
|
||||
// The details panel belongs to the message it was opened on: the next
|
||||
// message opens without it (mail-7).
|
||||
const shownEmailId = email?.id ?? null;
|
||||
const [details, setDetails] = useState<{ emailId: string | null; open: boolean }>({ emailId: shownEmailId, open: false });
|
||||
if (details.emailId !== shownEmailId) setDetails({ emailId: shownEmailId, open: false });
|
||||
const showFullHeaders = details.open && details.emailId === shownEmailId;
|
||||
const setShowFullHeaders = (open: boolean) => setDetails({ emailId: shownEmailId, open });
|
||||
const [isDownloadingAll, setIsDownloadingAll] = useState(false);
|
||||
const [imageThumbUrls, setImageThumbUrls] = useState<Record<string, string>>({});
|
||||
const [allowExternalContent, setAllowExternalContent] = useState(false);
|
||||
@@ -5516,8 +5522,10 @@ ${printWatermark ? printWatermark.html : ''}
|
||||
};
|
||||
const replyToDifferent = !!email.replyTo?.length &&
|
||||
(!email.from || email.replyTo[0].email !== email.from[0]?.email);
|
||||
// Received before it was sent means a wrong clock somewhere, not a
|
||||
// delivery time (mail-29).
|
||||
const deliveryDeltaMs = email.sentAt && email.receivedAt
|
||||
? Math.abs(new Date(email.receivedAt).getTime() - new Date(email.sentAt).getTime())
|
||||
? new Date(email.receivedAt).getTime() - new Date(email.sentAt).getTime()
|
||||
: 0;
|
||||
const formatDelta = (diff: number) => {
|
||||
const minutes = Math.floor(diff / 60000);
|
||||
|
||||
Reference in New Issue
Block a user