Merge pull request #35861 from overleaf/jel-email-notifications-domain-capture

[web] Use name in greeting for email notifications for domain capture

GitOrigin-RevId: 56e46bc6de031656e4e8c0a9332f6b94aa517506
This commit is contained in:
Jessica Lawshe
2026-09-17 11:42:20 +00:00
committed by Copybot
parent 12e02cc69a
commit 16a5c45b5f
4 changed files with 72 additions and 0 deletions
@@ -662,6 +662,10 @@ templates.domainReverificationFailed = ctaTemplate({
}
return `${domain} needs re-verifying`
},
greeting(opts, isPlainText) {
const greeting = opts.firstName ? `Hi ${opts.firstName},` : 'Hi,'
return EmailMessageHelper.cleanHTML(greeting, isPlainText)
},
message(opts) {
const domain = _.escape(opts.domain)
if (opts.capturedByGroup) {
@@ -1307,6 +1311,10 @@ templates.groupDomainCapturedByGroupChanged = ctaTemplate({
? `Domain capture is active for ${_.escape(opts.domain)}`
: `Domain capture is inactive for ${_.escape(opts.domain)}`
},
greeting(opts, isPlainText) {
const greeting = opts.firstName ? `Hi ${opts.firstName},` : 'Hi,'
return EmailMessageHelper.cleanHTML(greeting, isPlainText)
},
message(opts) {
if (opts.domainCapturedByGroup) {
return [
@@ -1334,6 +1342,10 @@ templates.domainVerifiedForGroup = NoCTAEmailTemplate({
return 'Your domain is verified — ready to capture?'
}
},
greeting(opts, isPlainText) {
const greeting = opts.firstName ? `Hi ${opts.firstName},` : 'Hi,'
return EmailMessageHelper.cleanHTML(greeting, isPlainText)
},
message(opts, isPlainText) {
const message = [
`We've verified <b>${_.escape(opts.domain)}</b> for your Overleaf group.`,
@@ -84,6 +84,8 @@ class EmailExistsError extends OErrorV2CompatibleError {
class InvalidError extends BackwardCompatibleError {}
class NotifyGroupAdminError extends OError {}
class NotInV2Error extends BackwardCompatibleError {}
class SLInV2Error extends BackwardCompatibleError {}
@@ -415,6 +417,7 @@ module.exports = {
UnconfirmedEmailError,
EmailExistsError,
InvalidError,
NotifyGroupAdminError,
NotInV2Error,
OutputFileFetchFailedError,
SAMLAssertionAudienceMismatch,
@@ -1140,6 +1140,7 @@ describe('EmailBuilder', function () {
expect(email.html).to.contain(
'Domain capture is active for example.com'
)
expect(email.text).to.contain('Hi,')
expect(email.html).to.contain('/manage/groups/group-123/settings')
expect(email.text).to.contain('/manage/groups/group-123/settings')
})
@@ -1164,6 +1165,22 @@ describe('EmailBuilder', function () {
expect(email.html).to.contain('admin@overleaf.test')
expect(email.text).to.contain('admin@overleaf.test')
})
it('should use firstName in greeting when provided', function (ctx) {
const email = ctx.EmailBuilder.buildEmail(
'groupDomainCapturedByGroupChanged',
{
to: 'admin@example.com',
groupId: 'group-123',
domainCapturedByGroup: true,
domain: 'example.com',
firstName: 'Ada',
}
)
expect(email.text).to.contain('Hi Ada,')
expect(email.html).to.contain('Hi Ada,')
})
})
describe('domainVerifiedForGroup', function () {
@@ -1175,6 +1192,7 @@ describe('EmailBuilder', function () {
})
expect(email.subject).to.equal('Your domain is verified')
expect(email.text).to.contain('Hi,')
expect(email.html).to.contain("We've verified")
expect(email.html).to.contain('<b>example.com</b>')
expect(email.html).to.contain(
@@ -1199,6 +1217,18 @@ describe('EmailBuilder', function () {
"You'll receive a confirmation email once the capture is active."
)
})
it('should use firstName in greeting when provided', function (ctx) {
const email = ctx.EmailBuilder.buildEmail('domainVerifiedForGroup', {
to: 'admin@example.com',
domain: 'example.com',
capturedByGroup: true,
firstName: 'Ada',
})
expect(email.text).to.contain('Hi Ada,')
expect(email.html).to.contain('Hi Ada,')
})
})
describe('domainReverificationFailed', function () {
@@ -1244,6 +1274,23 @@ describe('EmailBuilder', function () {
expect(ctx.email.html).to.contain(ctx.opts.domainSettingsUrl)
expect(ctx.email.text).to.contain(ctx.opts.domainSettingsUrl)
})
it('uses the default greeting when firstName is not provided', function (ctx) {
expect(ctx.email.text).to.contain('Hi,')
})
it('uses firstName in the greeting when provided', function (ctx) {
const email = ctx.EmailBuilder.buildEmail(
'domainReverificationFailed',
{
...ctx.opts,
firstName: 'Ada',
}
)
expect(email.text).to.contain('Hi Ada,')
expect(email.html).to.contain('Hi Ada,')
})
})
describe('when the domain is not captured by the group', function () {
@@ -43,3 +43,13 @@ describe('BackwardCompatibleError', function () {
expect(error.cause).to.equal(cause)
})
})
describe('NotifyGroupAdminError', function () {
it('is exported and can be constructed', function () {
const error = new Errors.NotifyGroupAdminError('notify admin failed')
expect(error).to.be.instanceOf(Error)
expect(error).to.be.instanceOf(Errors.NotifyGroupAdminError)
expect(error.message).to.equal('notify admin failed')
})
})