Files
pn-new-crm/tests/unit/services/signing-status-notification.service.test.ts

83 lines
2.8 KiB
TypeScript
Raw Normal View History

import { beforeEach, describe, expect, it, vi } from 'vitest';
// Boundaries the sender depends on — mock the I/O edges, exercise the real
// wiring (recipient resolution → template → per-recipient send).
vi.mock('@/lib/email', () => ({ sendEmail: vi.fn().mockResolvedValue(undefined) }));
vi.mock('@/lib/email/branding-resolver', () => ({
getBrandingShell: vi.fn().mockResolvedValue(null),
}));
vi.mock('@/lib/services/notification-recipients', () => ({
resolveNotificationRecipients: vi.fn(),
}));
import { sendEmail } from '@/lib/email';
import { resolveNotificationRecipients } from '@/lib/services/notification-recipients';
import { sendSigningStatusNotification } from '@/lib/services/document-signing-emails.service';
const mockSendEmail = vi.mocked(sendEmail);
const mockResolve = vi.mocked(resolveNotificationRecipients);
const baseArgs = {
portId: 'port-1',
portName: 'Port Nimara',
event: 'signed' as const,
documentLabel: 'Expression of Interest',
clientName: 'Jane Doe',
crmUrl: 'https://crm.portnimara.com/port-nimara/documents/abc',
signerName: 'Jane Doe',
signerRole: 'client' as const,
signedCount: 1,
totalCount: 3,
};
describe('sendSigningStatusNotification', () => {
beforeEach(() => {
mockSendEmail.mockClear();
mockResolve.mockReset();
});
it('emails every configured recipient when a signer signs', async () => {
mockResolve.mockResolvedValue(['admin@portnimara.com', 'sales@portnimara.com']);
await sendSigningStatusNotification(baseArgs);
// Resolves from the signing list, falling back to the reply-to address.
expect(mockResolve).toHaveBeenCalledWith(
'port-1',
'signing_notification_recipients',
'email_reply_to',
);
expect(mockSendEmail).toHaveBeenCalledTimes(2);
const recipients = mockSendEmail.mock.calls.map((c) => c[0]);
expect(recipients).toContain('admin@portnimara.com');
expect(recipients).toContain('sales@portnimara.com');
// Subject reflects who signed.
const subject = mockSendEmail.mock.calls[0]?.[1] as string;
expect(subject).toContain('Jane Doe');
// portId threaded through so per-port From + redirect apply.
expect(mockSendEmail.mock.calls[0]?.[5]).toBe('port-1');
});
it('sends nothing when no recipients are configured or resolvable', async () => {
mockResolve.mockResolvedValue([]);
await sendSigningStatusNotification(baseArgs);
expect(mockSendEmail).not.toHaveBeenCalled();
});
it('uses the completion subject for the completed event', async () => {
mockResolve.mockResolvedValue(['sales@portnimara.com']);
await sendSigningStatusNotification({
...baseArgs,
event: 'completed',
signerName: null,
});
const subject = mockSendEmail.mock.calls[0]?.[1] as string;
expect(subject.toLowerCase()).toContain('fully signed');
expect(subject).toContain('Jane Doe');
});
});