import { DEFAULT_WELCOME_MAIL_TEXTS, findUnknownWelcomeMailPlaceholders } from '@tessera/shared'; import { describe, expect, it } from 'vitest'; import { applyWelcomeMailPlaceholders, firstNameOf, renderWelcomeMail, type WelcomeMailInput, } from './welcome-mail.template'; /** * Willkommensmail mit eigener Vorlage: Platzhalter, Escaping, Absaetze, * feste Bausteine. */ const base: WelcomeMailInput = { name: 'Max Muster', username: 'max.muster', email: 'max@example.invalid', tenantName: 'Muster & Co', appUrl: 'https://tessera.example.invalid/', account: { kind: 'directory' }, headerImageSrc: null, }; /** Standard-Anmeldehinweise, damit Vorlagen im Test vollstaendig sind. */ const hints = { loginHintDirectory: DEFAULT_WELCOME_MAIL_TEXTS.loginHintDirectory, loginHintLocal: DEFAULT_WELCOME_MAIL_TEXTS.loginHintLocal, }; const localAccount = { kind: 'local', setPasswordUrl: 'https://tessera.example.invalid/reset-password/abc', validHours: 168, } as const; const values = { name: 'Max Muster', vorname: 'Max', benutzername: 'max.muster', email: 'max@example.invalid', adresse: 'https://t.example.invalid', firma: 'Muster & Co', }; describe('Platzhalter', () => { it('ersetzt alle sechs, Gross-/Kleinschreibung und Leerraum egal; unbekannte bleiben stehen', () => { expect( applyWelcomeMailPlaceholders( '{{name}}|{{vorname}}|{{benutzername}}|{{email}}|{{adresse}}|{{firma}}|{{ NAME }}|{{xyz}}', values, ), ).toBe( 'Max Muster|Max|max.muster|max@example.invalid|https://t.example.invalid|Muster & Co|Max Muster|{{xyz}}', ); }); it('findUnknownWelcomeMailPlaceholders nennt jeden unbekannten einmal', () => { expect(findUnknownWelcomeMailPlaceholders('{{a}} {{name}} {{a}} {{ b }} {{}}')).toEqual([ '{{a}}', '{{b}}', '{{}}', ]); expect(findUnknownWelcomeMailPlaceholders(DEFAULT_WELCOME_MAIL_TEXTS.heading)).toEqual([]); }); it('vorname: erstes Wort des Anzeigenamens, sonst Benutzername', () => { expect(firstNameOf(' Erika Mustermann ', 'erika')).toBe('Erika'); expect(firstNameOf(null, 'erika')).toBe('erika'); expect(firstNameOf(' ', 'erika')).toBe('erika'); }); }); describe('renderWelcomeMail mit Vorlage', () => { it('ohne Vorlage: Standard-Betreff und -Ueberschrift', () => { const mail = renderWelcomeMail(base); expect(mail.subject).toBe('Willkommen bei Tessera'); expect(mail.html).toContain('Willkommen bei Tessera, Max Muster!'); expect(mail.html).toContain('Viel Erfolg mit Tessera!'); }); it('Text ist reiner Text: HTML aus Vorlage und Werten wird escaped; Firma mit & korrekt', () => { const mail = renderWelcomeMail({ ...base, name: '', texts: { subject: 'Hallo\r\n{{firma}}', heading: '

{{name}}

', intro: 'klick', ...hints, closing: '', }, }); expect(mail.subject).toBe('Hallo Muster & Co'); expect(mail.html).not.toContain(''); expect(mail.html).not.toContain('

'); expect(mail.html).not.toContain('href="javascript:'); expect(mail.html).toContain('<h2><script>x</script></h2>'); expect(mail.html).toContain('Hallo Muster & Co'); }); it('Leerzeile = neuer Absatz, einfacher Umbruch =
; leerer Abschluss faellt weg', () => { const mail = renderWelcomeMail({ ...base, texts: { subject: 's', heading: 'h', intro: 'A1\nA2\r\n\r\n\nB', ...hints, closing: ' ' }, }); expect(mail.html).toContain('>A1
A2

'); expect(mail.html).toContain('>B

'); expect(mail.html).not.toContain('Viel Erfolg'); expect(mail.text).toContain('A1\nA2\n\nB'); }); it('feste Bausteine bleiben bei jeder Vorlage: Adresse, Benutzername, Anmeldehinweis, Zu Tessera, Fusszeile', () => { const mail = renderWelcomeMail({ ...base, account: { kind: 'local', setPasswordUrl: 'https://tessera.example.invalid/reset-password/abc', validHours: 168, }, texts: { subject: 'x', heading: 'y', intro: '', ...hints, closing: '' }, }); expect(mail.html).toContain('https://tessera.example.invalid/reset-password/abc'); expect(mail.html).toContain('Passwort festlegen'); expect(mail.html).toContain('7 Tage'); expect(mail.html).toContain('Zu Tessera'); expect(mail.html).toContain('max.muster'); expect(mail.html).toContain('im Auftrag Ihres Administrators'); }); it('local-example: Knopf zur Anmeldeseite mit Beispiel-Hinweis, kein reset-password', () => { const mail = renderWelcomeMail({ ...base, account: { kind: 'local-example' }, notice: 'Testmail', }); expect(mail.html).toContain('Beispiel-Link'); expect(mail.html).not.toContain('reset-password'); expect(mail.html).toContain('Testmail'); expect(mail.text.startsWith('[Testmail]')).toBe(true); }); }); describe('Anmeldehinweis je Kontoart', () => { it('Standard: Verzeichniskonto bekommt den Windows-Hinweis, lokales Konto den Hinweis vor dem Knopf', () => { const dir = renderWelcomeMail(base); expect(dir.html).toContain('gewohnten Windows-Passwort'); expect(dir.html).not.toContain('persönliches Passwort fest'); const local = renderWelcomeMail({ ...base, account: localAccount }); expect(local.html).toContain('legen Sie bitte Ihr persönliches Passwort fest'); expect(local.html).not.toContain('Windows-Passwort'); // Hinweis steht vor dem Knopf expect(local.html.indexOf('persönliches Passwort fest')).toBeLessThan( local.html.indexOf('Passwort festlegen'), ); }); it('eigener Text je Kontoart: Platzhalter ersetzt, escaped, Leerzeile = Absatz; Knopf und Gueltigkeit bleiben fest', () => { const texts = { ...DEFAULT_WELCOME_MAIL_TEXTS, loginHintDirectory: 'Anmeldung als {{benutzername}} mit Firmenkennwort.\n\nFragen? IT anrufen.', loginHintLocal: 'Hallo {{vorname}}, bitte zuerst ein Passwort setzen.', }; const dir = renderWelcomeMail({ ...base, texts }); expect(dir.html).toContain( 'Anmeldung als max.muster mit <b>Firmenkennwort</b>.

', ); expect(dir.html).toContain('>Fragen? IT anrufen.

'); expect(dir.html).not.toContain('Windows-Passwort'); expect(dir.html).not.toContain('bitte zuerst ein Passwort setzen'); expect(dir.text).toContain( 'Anmeldung als max.muster mit Firmenkennwort.\n\nFragen? IT anrufen.', ); const local = renderWelcomeMail({ ...base, account: localAccount, texts }); expect(local.html).toContain('Hallo Max, bitte zuerst ein Passwort setzen.'); expect(local.html).not.toContain('Firmenkennwort'); expect(local.html).toContain('reset-password/abc'); expect(local.html).toContain('7 Tage'); expect(local.text).toContain('Hallo Max, bitte zuerst ein Passwort setzen.'); const example = renderWelcomeMail({ ...base, account: { kind: 'local-example' }, texts }); expect(example.html).toContain('Hallo Max, bitte zuerst ein Passwort setzen.'); }); it('leerer oder fehlender Hinweis (aeltere Aufrufer) → Standardtext', () => { const { loginHintDirectory: _d, loginHintLocal: _l, ...old } = DEFAULT_WELCOME_MAIL_TEXTS; const dir = renderWelcomeMail({ ...base, texts: old as any }); expect(dir.html).toContain('gewohnten Windows-Passwort'); const local = renderWelcomeMail({ ...base, account: localAccount, texts: { ...DEFAULT_WELCOME_MAIL_TEXTS, loginHintLocal: ' ' }, }); expect(local.html).toContain('persönliches Passwort fest'); }); it('local-no-link behaelt den festen Hinweis', () => { const mail = renderWelcomeMail({ ...base, account: { kind: 'local-no-link' } }); expect(mail.html).toContain('Ihr Startpasswort erhalten Sie von Ihrem Administrator.'); }); }); describe('Kopf (Outlook ohne CID-Bild)', () => { it('Wellenstreifen 600x40 in einer Zelle mit dunkler Kopffarbe, Inhalt beginnt mit 24px Abstand', () => { const mail = renderWelcomeMail({ ...base, headerImageSrc: 'cid:welcome-header@tessera' }); expect(mail.html).toMatch( /\n