import { test } from 'node:test';
import assert from 'node:assert/strict';
import { isEmail, mailLayout, esc } from '../src/lib/mail.ts';
test('Valid addresses are recognised', () => {
for (const good of ['first.last@example.com', 'a.b+c@sub.example.co.uk', 'x@y.de', 'Gran_1949@example.de']) {
assert.ok(isEmail(good), `wrongly rejected: ${good}`);
}
});
test('Invalid addresses are rejected — including the ones with a line break appended', () => {
const bad = [
'', ' ', 'not-an-address', 'a@b', '@b.de', 'a@.de', 'a b@c.de', 'a@b .de',
'a@b.de\nBcc: victim@example.com', // header injection
'a@b.de\r\nSubject: x',
'a@b.de\n', // JavaScript's "$" also matches before a line end
'a'.repeat(250) + '@b.de', // too long
];
for (const s of bad) assert.ok(!isEmail(s), `slipped through: ${JSON.stringify(s)}`);
});
test('esc defuses everything that could take the HTML apart', () => {
assert.equal(esc('x'), '<b>x</b>');
assert.equal(esc('a & b'), 'a & b');
assert.equal(esc('said "hello"'), 'said "hello"');
// Order: & first, otherwise < would become <
assert.equal(esc('<&>'), '<&>');
});
test('The heading of the mail is always escaped', () => {
const html = mailLayout('', ['Text']);
assert.ok(!html.includes('' });
assert.ok(!html.includes('