mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 08:13:50 +00:00
# Conflicts: # CONTRIBUTING.md # LICENSE # README.md # app/api/calendar-agenda/route.ts # app/api/wopi/files/[fileId]/contents/route.ts # app/api/wopi/launch/route.ts # components/calendar/calendar-app.tsx # components/calendar/calendar-day-view.tsx # components/calendar/calendar-month-view.tsx # components/calendar/calendar-week-view.tsx # components/calendar/event-modal.tsx # components/contacts/contact-detail.tsx # components/email/email-composer.tsx # components/email/email-list.tsx # components/files/file-browser.tsx # components/files/files-app.tsx # components/files/folder-tree-sidebar.tsx # components/filters/filter-rule-modal.tsx # components/mail/mail-app.tsx # hooks/use-mailbox-drop.ts # integration/run-tests.sh # lib/__tests__/calendar-agenda-route.test.ts # lib/__tests__/jmap-draft-lifecycle.test.ts # lib/__tests__/web-push.test.ts # lib/account-state-manager.ts # lib/jmap/client-interface.ts # lib/jmap/client.ts # lib/jmap/types.ts # lib/message-list-order.ts # lib/sieve/generator.ts # lib/sieve/parser.ts # lib/telemetry/payload.ts # lib/thread-utils.ts # lib/unified-mailbox.ts # lib/web-push.ts # lib/wopi/token.ts # locales/ar/common.json # locales/ca/common.json # locales/cs/common.json # locales/da/common.json # locales/de/common.json # locales/en/common.json # locales/es/common.json # locales/fa/common.json # locales/fr/common.json # locales/he/common.json # locales/hu/common.json # locales/it/common.json # locales/ja/common.json # locales/ko/common.json # locales/lv/common.json # locales/mn/common.json # locales/nb/common.json # locales/nl/common.json # locales/pl/common.json # locales/pt/common.json # locales/ro/common.json # locales/ru/common.json # locales/sk/common.json # locales/tr/common.json # locales/uk/common.json # locales/zh-TW/common.json # locales/zh/common.json # scripts/lite/lib.mjs # stores/__tests__/calendar-store-synthetic-ids.test.ts # stores/__tests__/email-store-shared-folder-keywords.test.ts # stores/__tests__/email-store-unified-actions.test.ts # stores/__tests__/file-store.test.ts # stores/email-store.ts # stores/file-store.ts # stores/message-list-tabs-store.ts
123 lines
6.8 KiB
TypeScript
123 lines
6.8 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { NoLongerScheduledError, RecipientsRejectedError, RequestTimeoutError, ScheduleTooLateError, SubmissionQuotaError, formatRejectedRecipients } from '../jmap/client';
|
|
import { SendCancelledCleanupError, cancelFailureToast, partlyRefusedWarning, rescheduleFailureToast, sendFailureMessage } from '../send-failure-message';
|
|
|
|
// Keys of a namespace, returned with their values so the test sees which
|
|
// message was chosen and what went into it.
|
|
const translator = (namespace: string) => (key: string, values?: Record<string, string>) =>
|
|
values ? `${namespace}.${key} ${JSON.stringify(values)}` : `${namespace}.${key}`;
|
|
const t = translator('email_composer');
|
|
const tViewer = translator('email_viewer');
|
|
|
|
const STALWART_QUOTA = 'There are too many email submissions, please delete some before adding a new one.';
|
|
|
|
describe('sendFailureMessage', () => {
|
|
it('explains a full submission quota instead of showing the server text on Stalwart 1.0', () => {
|
|
const error = new SubmissionQuotaError(STALWART_QUOTA, true);
|
|
|
|
expect(sendFailureMessage(error, t)).toBe('email_composer.submission_quota');
|
|
});
|
|
|
|
it("gives another server's overQuota its own description, not Stalwart's explanation", () => {
|
|
expect(sendFailureMessage(new SubmissionQuotaError('Daily sending limit exceeded'), t))
|
|
.toBe('email_composer.submission_limit {"reason":"Daily sending limit exceeded"}');
|
|
expect(sendFailureMessage(new SubmissionQuotaError(undefined, false), t))
|
|
.toBe('email_composer.submission_limit {"reason":"overQuota"}');
|
|
});
|
|
|
|
it('points at Sent after a timeout and names the hold limit', () => {
|
|
expect(sendFailureMessage(new RequestTimeoutError(30_000), t)).toBe('email_composer.send_timeout');
|
|
expect(sendFailureMessage(new ScheduleTooLateError(604800), t)).toBe('email_composer.schedule_send_too_late');
|
|
});
|
|
|
|
it("shows other errors' own text, and a generic message when there is none", () => {
|
|
expect(sendFailureMessage(new Error('Envelope mailFrom does not match identity email address. [forbiddenFrom]'), t))
|
|
.toBe('Envelope mailFrom does not match identity email address. [forbiddenFrom]');
|
|
expect(sendFailureMessage(new Error(''), t)).toBe('email_composer.send_failed');
|
|
expect(sendFailureMessage('nope', t)).toBe('email_composer.send_failed');
|
|
});
|
|
|
|
// Stalwart 0.16 and 1.0 take the submission and refuse the recipients at
|
|
// RCPT, each with its own reply.
|
|
it('says the message was not sent and names every refused recipient with its reply', () => {
|
|
const error = new RecipientsRejectedError([
|
|
{ email: 'nobody@example.net', smtpReply: '550 5.1.2 Relay not allowed.' },
|
|
{ email: 'busy@example.org', smtpReply: '452 4.4.5 Rate limit exceeded, try again later.' },
|
|
]);
|
|
|
|
expect(sendFailureMessage(error, t)).toBe(
|
|
'email_composer.recipients_refused {"recipients":"nobody@example.net (550 5.1.2 Relay not allowed.), busy@example.org (452 4.4.5 Rate limit exceeded, try again later.)"}',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('formatRejectedRecipients', () => {
|
|
it('lists an address without its reply when the server gave none', () => {
|
|
expect(formatRejectedRecipients([{ email: 'a@example.net', smtpReply: '' }, { email: 'b@example.net', smtpReply: '550 5.1.1 No such user' }]))
|
|
.toBe('a@example.net, b@example.net (550 5.1.1 No such user)');
|
|
});
|
|
});
|
|
|
|
describe('rescheduleFailureToast', () => {
|
|
it('says the message is still scheduled, and when, at a full quota on Stalwart 1.0', () => {
|
|
const error = new SubmissionQuotaError(STALWART_QUOTA, true);
|
|
|
|
expect(rescheduleFailureToast(error, tViewer, t, 'Sep 26, 10:00')).toEqual({
|
|
title: 'email_viewer.still_scheduled_at {"date":"Sep 26, 10:00"}',
|
|
message: 'email_composer.submission_quota',
|
|
});
|
|
expect(rescheduleFailureToast(error, tViewer, t).title).toBe('email_viewer.still_scheduled');
|
|
});
|
|
|
|
it('says the message went out, or is no longer scheduled, when the original left the queue meanwhile', () => {
|
|
expect(rescheduleFailureToast(new NoLongerScheduledError(true), tViewer, t, 'Sep 26, 10:00'))
|
|
.toEqual({ title: 'email_viewer.already_sent' });
|
|
expect(rescheduleFailureToast(new NoLongerScheduledError(false), tViewer, t))
|
|
.toEqual({ title: 'email_viewer.no_longer_scheduled' });
|
|
});
|
|
|
|
// The original is kept for all of its recipients, so the headline must not
|
|
// read as "not sent", which invites sending the message a second time.
|
|
it('leads with the kept schedule when the server refused recipients of the replacement (Stalwart 0.16 and 1.0)', () => {
|
|
const error = new RecipientsRejectedError([{ email: 'bob@example.org', smtpReply: '452 4.4.5 Rate limit exceeded, try again later.' }]);
|
|
|
|
expect(rescheduleFailureToast(error, tViewer, t, 'Sep 26, 10:00')).toEqual({
|
|
title: 'email_viewer.still_scheduled_at {"date":"Sep 26, 10:00"}',
|
|
message: 'email_viewer.reschedule_recipients_refused {"recipients":"bob@example.org (452 4.4.5 Rate limit exceeded, try again later.)"}',
|
|
});
|
|
expect(rescheduleFailureToast(error, tViewer, t).title).toBe('email_viewer.still_scheduled');
|
|
});
|
|
|
|
it('falls back to the send failure message otherwise', () => {
|
|
expect(rescheduleFailureToast(new RequestTimeoutError(30_000), tViewer, t, 'Sep 26, 10:00'))
|
|
.toEqual({ title: 'email_composer.send_timeout' });
|
|
expect(rescheduleFailureToast(new Error('cannotUnsend'), tViewer, t)).toEqual({ title: 'cannotUnsend' });
|
|
});
|
|
});
|
|
|
|
describe('partlyRefusedWarning', () => {
|
|
it('names the recipients the server refused while it took the others', () => {
|
|
expect(partlyRefusedWarning({ rejectedRecipients: [{ email: 'nobody@example.net', smtpReply: '550 5.1.2 Relay not allowed.' }] }, t))
|
|
.toBe('email_composer.recipients_partly_refused {"recipients":"nobody@example.net (550 5.1.2 Relay not allowed.)"}');
|
|
});
|
|
|
|
it('says nothing when the server took every recipient', () => {
|
|
expect(partlyRefusedWarning({}, t)).toBeNull();
|
|
expect(partlyRefusedWarning({ rejectedRecipients: [] }, t)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('cancelFailureToast', () => {
|
|
it('reports a failed cancel with its reason: the mail may still go out', () => {
|
|
expect(cancelFailureToast(new Error('The requested message is no longer in the queue.'), tViewer, 'Undo send failed'))
|
|
.toEqual({ title: 'Undo send failed', message: 'The requested message is no longer in the queue.' });
|
|
});
|
|
|
|
it('says the send was cancelled when only a step after the cancel failed', () => {
|
|
expect(cancelFailureToast(new SendCancelledCleanupError('restore', new Error('503')), tViewer, 'Undo send failed'))
|
|
.toEqual({ title: 'email_viewer.send_cancelled_restore_failed' });
|
|
expect(cancelFailureToast(new SendCancelledCleanupError('remove', new Error('503')), tViewer, 'Undo send failed'))
|
|
.toEqual({ title: 'email_viewer.send_cancelled_remove_failed' });
|
|
});
|
|
});
|