0df05c83c6
* fix(email): calm, correctly-signed consent expiry notification The consent expiry email was signed with the recipient's own company name instead of the app, used red alarm chrome (header pill + button), and never said why the recipient got it. After the #1271 health probe drained a backlog of 25 dead sessions in one 05:00 cron run, that design read as phishing to a batch of users at once. - Sign off as the app; the company the connection belongs to moves into a details row and the why-did-I-get-this footer - Drop all red/orange chrome; neutral editorial layout, pill button - Explain that PSD2 consent expiry is routine and that no data is lost - Show the destination URL as plain text next to the button - Calmer subjects (renewal framing instead of 'synkronisering stoppad') - Reply-to support instead of dead-ending at noreply - Add template tests (was untested) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(enable-banking): pause consent expiry emails behind env flag Founder call 2026-07-29: the in-app surfaces already flag a dead connection, so the cron email adds noise. Status transitions keep running; set BANK_CONSENT_EXPIRY_EMAILS=true to resume sending. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
91 lines
3.4 KiB
TypeScript
91 lines
3.4 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import {
|
|
generateConsentExpiryEmailHtml,
|
|
generateConsentExpiryEmailText,
|
|
generateConsentExpiryEmailSubject,
|
|
} from '@/lib/email/consent-notification-templates'
|
|
import { getBranding } from '@/lib/branding/service'
|
|
|
|
const expired = {
|
|
bankName: 'SEB',
|
|
daysUntilExpiry: 0,
|
|
renewalUrl: 'https://app.gnubok.se/settings/banking',
|
|
companyName: 'Glimworks AB',
|
|
isExpired: true,
|
|
}
|
|
|
|
const expiringSoon = {
|
|
...expired,
|
|
daysUntilExpiry: 3,
|
|
isExpired: false,
|
|
}
|
|
|
|
describe('consent expiry email templates', () => {
|
|
it('signs off as the app, never as the recipient company', () => {
|
|
const html = generateConsentExpiryEmailHtml(expired)
|
|
const text = generateConsentExpiryEmailText(expired)
|
|
const { appName } = getBranding()
|
|
|
|
expect(html).toContain(`Med vänliga hälsningar,<br>\n <strong>${appName}</strong>`)
|
|
expect(text).toContain(`Med vänliga hälsningar,\n${appName}`)
|
|
expect(html).not.toContain('Med vänliga hälsningar,<br>\n <strong>Glimworks')
|
|
})
|
|
|
|
it('uses no alarm colors in the chrome', () => {
|
|
for (const data of [expired, expiringSoon]) {
|
|
const html = generateConsentExpiryEmailHtml(data)
|
|
expect(html).not.toMatch(/#dc2626|#ea580c|#ef4444|#b91c1c/i)
|
|
expect(html).not.toContain('Åtgärd krävs')
|
|
}
|
|
})
|
|
|
|
it('names the bank, the company, and the destination URL', () => {
|
|
const html = generateConsentExpiryEmailHtml(expired)
|
|
expect(html).toContain('SEB')
|
|
expect(html).toContain('Glimworks AB')
|
|
// The URL is shown as plain text next to the button so the recipient can
|
|
// verify where it leads before clicking.
|
|
const urlMentions = html.split(expired.renewalUrl).length - 1
|
|
expect(urlMentions).toBeGreaterThanOrEqual(2)
|
|
})
|
|
|
|
it('explains why the recipient got the email', () => {
|
|
const html = generateConsentExpiryEmailHtml(expired)
|
|
const text = generateConsentExpiryEmailText(expired)
|
|
const { supportEmail } = getBranding()
|
|
expect(html).toContain('Du får det här mejlet eftersom')
|
|
expect(html).toContain(supportEmail)
|
|
expect(text).toContain('Du får det här mejlet eftersom')
|
|
expect(text).toContain(supportEmail)
|
|
})
|
|
|
|
it('omits the company row cleanly when no company name is available', () => {
|
|
const data = { ...expired, companyName: '' }
|
|
const html = generateConsentExpiryEmailHtml(data)
|
|
const text = generateConsentExpiryEmailText(data)
|
|
expect(html).not.toContain('Företag')
|
|
expect(html).not.toContain('för </')
|
|
expect(text).not.toContain('Företag:')
|
|
expect(generateConsentExpiryEmailSubject(data)).toBe('Förnya bankkopplingen till SEB')
|
|
})
|
|
|
|
it('generates calm, specific subjects', () => {
|
|
expect(generateConsentExpiryEmailSubject(expired)).toBe(
|
|
'Förnya bankkopplingen till SEB - Glimworks AB'
|
|
)
|
|
expect(generateConsentExpiryEmailSubject(expiringSoon)).toBe(
|
|
'Bankkopplingen till SEB löper ut om 3 dagar - Glimworks AB'
|
|
)
|
|
expect(generateConsentExpiryEmailSubject({ ...expiringSoon, daysUntilExpiry: 1 })).toBe(
|
|
'Bankkopplingen till SEB löper ut om 1 dag - Glimworks AB'
|
|
)
|
|
})
|
|
|
|
it('pluralizes days in the expiring-soon body', () => {
|
|
const html = generateConsentExpiryEmailHtml({ ...expiringSoon, daysUntilExpiry: 1 })
|
|
expect(html).toContain('löper ut om 1 dag')
|
|
const html3 = generateConsentExpiryEmailHtml(expiringSoon)
|
|
expect(html3).toContain('löper ut om 3 dagar')
|
|
})
|
|
})
|