Files
Linus Rath cbd23db2fe fix: give mail import and export their own settings section and name the upload limit as the server does
- Settings > Backup & reset gets a Mail files section for Import mail
  and Export all mail, instead of sitting under settings backup.
- The pause message names Stalwart's 50000000-byte quota as 50 MB, not
  48 MB, and the dialog and settings texts mention .ost files.
- The old import's leftovers go: expandImportableEmails and
  EML_IMPORT_ACCEPT in lib/eml-import.ts, and the strings
  email_viewer.import_email, mailbox_context_menu.import_email and
  notifications.import_email_success/_error in all 30 locales.

lib/__tests__/eml-import.test.ts loses the tests of the removed
functions; its #891 checks that an .eml and an .eml in a ZIP keep their
Date header now run against the import's own sources.
2026-11-01 07:05:02 +01:00

56 lines
2.3 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { readMessageDate } from '@/lib/eml-import';
import { emlSource, zipSource, type ImportItem } from '@/lib/mail-import/sources';
import { ZipWriter } from '@/lib/mail-export/zip-writer';
const emlFile = (name: string, content = 'raw email', type = 'message/rfc822') =>
new File([content], name, { type });
const opts = { skipBefore: 0, maxMessageBytes: 50_000_000 };
async function all(source: AsyncGenerator<ImportItem>): Promise<ImportItem[]> {
const out: ImportItem[] = [];
for await (const item of source) out.push(item);
return out;
}
describe('Date header as receivedAt (#891)', () => {
const CRLF = String.fromCharCode(13, 10);
const raw = [
'From: Alice <alice@example.com>',
'Subject: old mail',
'Date: Tue, 5 Mar 2019 09:30:00',
' +0100 (CET)',
'',
'Date: Wed, 1 Jan 2025 00:00:00 +0000',
'body',
].join(CRLF);
it('carries the Date header of an .eml file as a JMAP UTCDate', async () => {
const [item] = await all(emlSource(emlFile('old.eml', raw), opts));
expect(item).toMatchObject({ kind: 'message', receivedAt: '2019-03-05T08:30:00Z' });
});
it('carries the Date header of each .eml inside a .zip', async () => {
const parts: Uint8Array[] = [];
const z = new ZipWriter(async (c) => { parts.push(c.slice()); });
await z.addFile('old.eml', new TextEncoder().encode(raw));
await z.finish();
const [item] = await all(zipSource(new File(parts as BlobPart[], 'a.zip', { type: 'application/zip' }), opts));
expect(item).toMatchObject({ kind: 'message', receivedAt: '2019-03-05T08:30:00Z' });
});
it('leaves receivedAt unset when there is no usable Date header', async () => {
const bodyOnly = ['Subject: x', '', 'Date: Tue, 5 Mar 2019 09:30:00 +0100'].join(CRLF);
await expect(readMessageDate(new Blob([bodyOnly]))).resolves.toBeUndefined();
const garbage = ['Date: not a date', '', 'body'].join(CRLF);
await expect(readMessageDate(new Blob([garbage]))).resolves.toBeUndefined();
});
it('ignores a Date header far in the future', async () => {
const now = Date.parse('2026-01-01T00:00:00Z');
const future = ['Date: Fri, 1 Jan 2027 00:00:00 +0000', '', 'body'].join(CRLF);
await expect(readMessageDate(new Blob([future]), now)).resolves.toBeUndefined();
});
});