Merge pull request #2017 from mozilla/pb/1684-sentence-case-email-subjects

https://github.com/mozilla/fxa/pull/2017
r=shane-tomlinson
This commit is contained in:
Phil Booth 2019-08-01 13:41:54 +00:00 коммит произвёл GitHub
Родитель 46d706b953 1c2c3df858
Коммит c6962dc4c3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 95 добавлений и 90 удалений

Просмотреть файл

@ -187,6 +187,7 @@ module.exports = (log, db, config, customs, mailer) => {
account,
{
acceptLanguage: request.app.acceptLanguage,
numberRemaining: remainingRecoveryCodes,
uid: sessionToken.uid,
}
);

Просмотреть файл

@ -333,7 +333,11 @@ module.exports = function(log, config, oauthdb) {
return {
html: localized.html,
language: translator.language,
subject: translator.gettext(message.subject),
subject: translator.format(
translator.gettext(message.subject),
message.templateValues,
true
),
text: localized.text,
};
};
@ -660,7 +664,7 @@ module.exports = function(log, config, oauthdb) {
let templateName = 'verifyEmail';
const metricsTemplateName = templateName;
let subject = gettext('Verify your Firefox Account');
let subject = gettext('Verify Your Account');
const query = {
uid: message.uid,
code: message.code,
@ -738,11 +742,9 @@ module.exports = function(log, config, oauthdb) {
)}Email`;
let subject;
if (index < verificationReminders.keys.length - 1) {
subject = gettext('Reminder: Finish Creating Your Account');
subject = gettext('Reminder: Complete Registration');
} else {
subject = gettext(
'Final reminder: Confirm your email to activate your Firefox Account'
);
subject = gettext('Final Reminder: Activate Your Account');
}
templateNameToCampaignMap[template] = `${key}-verification-reminder`;
@ -806,7 +808,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Firefox Account authorization code'),
subject: gettext('Authorization Code: %(unblockCode)s'),
template: templateName,
templateValues: {
device: this._formatUserAgentInfo(message),
@ -863,12 +865,7 @@ module.exports = function(log, config, oauthdb) {
return oauthClientInfo.fetch(message.service).then(clientInfo => {
const clientName = clientInfo.name;
const subject = translator.format(
translator.gettext('Confirm new sign-in to %(clientName)s'),
{
clientName: clientName,
}
);
const subject = translator.gettext('Confirm New Sign-in');
return this.send(
Object.assign({}, message, {
@ -934,9 +931,10 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Sign-in code for Firefox'),
subject: gettext('Sign-in Code: %(code)s'),
template: templateName,
templateValues: {
code: message.code,
device: this._formatUserAgentInfo(message),
email: message.email,
ip: message.ip,
@ -995,7 +993,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Verify primary email'),
subject: gettext('Verify Primary Email'),
template: templateName,
templateValues: {
device: this._formatUserAgentInfo(message),
@ -1057,7 +1055,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Verify secondary email'),
subject: gettext('Verify Secondary Email'),
template: templateName,
templateValues: {
device: this._formatUserAgentInfo(message),
@ -1119,7 +1117,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Reset your Firefox Account password'),
subject: gettext('Reset Your Password'),
template: templateName,
templateValues: {
code: message.code,
@ -1157,7 +1155,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Your Firefox Account password has been changed'),
subject: gettext('Password Changed'),
template: templateName,
templateValues: {
device: this._formatUserAgentInfo(message),
@ -1193,7 +1191,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Firefox Account password changed'),
subject: gettext('Password Updated'),
template: templateName,
templateValues: {
privacyUrl: links.privacyUrl,
@ -1222,7 +1220,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Firefox Account password reset required'),
subject: gettext('Suspicious Activity: Password Reset Required'),
template: templateName,
templateValues: {
passwordManagerInfoUrl: links.passwordManagerInfoUrl,
@ -1248,12 +1246,7 @@ module.exports = function(log, config, oauthdb) {
return oauthClientInfo.fetch(message.service).then(clientInfo => {
const clientName = clientInfo.name;
const subject = translator.format(
translator.gettext('New sign-in to %(clientName)s'),
{
clientName: clientName,
}
);
const subject = translator.gettext('New Sign-in to %(clientName)s');
return this.send(
Object.assign({}, message, {
@ -1288,12 +1281,12 @@ module.exports = function(log, config, oauthdb) {
});
let templateName = 'postVerifyEmail';
let subject = gettext('Firefox Account verified');
let subject = gettext('Account Verified');
const query = {};
if (message.style === 'trailhead') {
templateName = 'postVerifyTrailheadEmail';
subject = gettext('Your Firefox Account is Confirmed');
subject = gettext('Account Confirmed');
query.style = 'trailhead';
}
@ -1344,7 +1337,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Secondary Firefox Account email added'),
subject: gettext('Secondary Email Added'),
template: templateName,
templateValues: {
androidLink: links.androidLink,
@ -1377,7 +1370,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Firefox Account new primary email'),
subject: gettext('New Primary Email'),
template: templateName,
templateValues: {
androidLink: links.androidLink,
@ -1410,7 +1403,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Secondary Firefox Account email removed'),
subject: gettext('Secondary Email Removed'),
template: templateName,
templateValues: {
androidLink: links.androidLink,
@ -1441,7 +1434,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Two-step authentication enabled'),
subject: gettext('Two-Step Authentication Enabled'),
template: templateName,
templateValues: {
androidLink: links.androidLink,
@ -1481,7 +1474,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Two-step authentication disabled'),
subject: gettext('Two-Step Authentication Disabled'),
template: templateName,
templateValues: {
androidLink: links.androidLink,
@ -1521,7 +1514,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('New recovery codes generated'),
subject: gettext('New Recovery Codes Generated'),
template: templateName,
templateValues: {
androidLink: links.androidLink,
@ -1561,7 +1554,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Recovery code consumed'),
subject: gettext('Recovery Code Used'),
template: templateName,
templateValues: {
androidLink: links.androidLink,
@ -1586,9 +1579,12 @@ module.exports = function(log, config, oauthdb) {
};
Mailer.prototype.lowRecoveryCodesEmail = function(message) {
const { numberRemaining } = message;
log.trace('mailer.lowRecoveryCodesEmail', {
email: message.email,
uid: message.uid,
numberRemaining,
});
const templateName = 'lowRecoveryCodesEmail';
@ -1598,15 +1594,23 @@ module.exports = function(log, config, oauthdb) {
'X-Link': links.link,
};
let subject;
if (numberRemaining === 1) {
subject = gettext('1 Recovery Code Remaining');
} else {
subject = gettext('%(numberRemaining)s Recovery Codes Remaining');
}
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Low recovery codes remaining'),
subject,
template: templateName,
templateValues: {
androidLink: links.androidLink,
iosLink: links.iosLink,
link: links.link,
numberRemaining,
privacyUrl: links.privacyUrl,
passwordChangeLinkAttributes: links.passwordChangeLinkAttributes,
passwordChangeLink: links.passwordChangeLink,
@ -1634,7 +1638,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Account recovery key generated'),
subject: gettext('Account Recovery Key Generated'),
template: templateName,
templateValues: {
androidLink: links.androidLink,
@ -1677,7 +1681,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Account recovery key removed'),
subject: gettext('Account Recovery Key Removed'),
template: templateName,
templateValues: {
androidLink: links.androidLink,
@ -1720,7 +1724,7 @@ module.exports = function(log, config, oauthdb) {
return this.send(
Object.assign({}, message, {
headers,
subject: gettext('Firefox Account password reset with recovery key'),
subject: gettext('Password Updated Using Recovery Key'),
template: templateName,
templateValues: {
androidLink: links.androidLink,

Просмотреть файл

@ -123,6 +123,7 @@ function sendMail(mailer, messageToSend) {
country: 'Spain',
},
locations: [],
numberRemaining: 2,
productId: '0123456789abcdef',
redirectTo: 'https://redirect.com/',
resume:

Просмотреть файл

@ -16,7 +16,12 @@ const TEST_EMAIL = 'test@email.com';
const UID = 'uid';
function runTest(routePath, requestOptions) {
const config = { recoveryCodes: {} };
const config = {
recoveryCodes: {
count: 8,
notifyLowCount: 2,
},
};
routes = require('../../../lib/routes/recovery-codes')(
log,
db,
@ -87,6 +92,17 @@ describe('recovery codes', () => {
});
describe('/session/verify/recoveryCode', () => {
it('sends email if recovery codes are low', async () => {
db.consumeRecoveryCode = sinon.spy(code => {
return P.resolve({ remaining: 1 });
});
await runTest('/session/verify/recoveryCode', requestOptions);
assert.equal(mailer.sendLowRecoveryCodeNotification.callCount, 1);
const args = mailer.sendLowRecoveryCodeNotification.args[0];
assert.lengthOf(args, 3);
assert.equal(args[2].numberRemaining, 1);
});
it('should rate-limit attempts to use a recovery code via customs', () => {
requestOptions.payload.code = '1234567890';
db.consumeRecoveryCode = sinon.spy(code => {

Просмотреть файл

@ -234,6 +234,7 @@ describe('lib/senders/email:', () => {
deviceId: 'foo',
email: 'a@b.com',
locations: [],
numberRemaining: 2,
productId: 'wibble',
service: 'sync',
tokenCode: 'abc123',
@ -568,6 +569,10 @@ describe('lib/senders/email:', () => {
assert.include(emailConfig.text, url);
assert.notInclude(emailConfig.html, 'utm_source=email');
assert.notInclude(emailConfig.text, 'utm_source=email');
if (type === 'lowRecoveryCodesEmail') {
assert.equal(emailConfig.subject, '2 Recovery Codes Remaining');
}
});
return mailer[type](message);
});
@ -751,7 +756,7 @@ describe('lib/senders/email:', () => {
it('test verify token email', () => {
mailer.mailer.sendMail = stubSendMail(emailConfig => {
const verifyLoginUrl = config.get('smtp').verifyLoginUrl;
assert.equal(emailConfig.subject, 'Confirm new sign-in to Firefox');
assert.equal(emailConfig.subject, 'Confirm New Sign-in');
assert.ok(emailConfig.html.indexOf(verifyLoginUrl) > 0);
assert.ok(emailConfig.text.indexOf(verifyLoginUrl) > 0);
});
@ -762,7 +767,7 @@ describe('lib/senders/email:', () => {
case 'newDeviceLoginEmail':
it('test new device login email', () => {
mailer.mailer.sendMail = stubSendMail(emailConfig => {
assert.equal(emailConfig.subject, 'New sign-in to Firefox');
assert.equal(emailConfig.subject, 'New Sign-in to Firefox');
});
return mailer[type](message);
});
@ -833,7 +838,7 @@ describe('lib/senders/email:', () => {
assert.include(emailConfig.text, 'utm_content=fx-confirm-email');
assert.equal(
emailConfig.subject,
'Reminder: Finish Creating Your Account'
'Reminder: Complete Registration'
);
});
return mailer[type](message);
@ -860,7 +865,7 @@ describe('lib/senders/email:', () => {
assert.include(emailConfig.text, 'utm_content=fx-confirm-email');
assert.equal(
emailConfig.subject,
'Final reminder: Confirm your email to activate your Firefox Account'
'Final Reminder: Activate Your Account'
);
});
return mailer[type](message);
@ -2494,11 +2499,8 @@ describe('email translations', () => {
'language header is correct'
);
// NOTE: translation might change, but we use the subject, we don't change that often.
assert.equal(
emailConfig.subject,
'أكّد حساب فَيَرفُكس الخاص بك',
'translation is correct'
);
// TODO: switch back to testing the subject when translations have caught up
assert.include(emailConfig.text, 'سياسة موزيلا للخصوصيّة');
});
return mailer['verifyEmail'](message);
@ -2514,14 +2516,13 @@ describe('email translations', () => {
'language header is correct'
);
// NOTE: translation might change, but we use the subject, we don't change that often.
assert.equal(
emailConfig.subject,
'Подтвердите ваш Аккаунт Firefox',
'translation is correct'
);
assert.equal(emailConfig.subject, 'Завершите создание вашего Аккаунта');
});
return mailer['verifyEmail'](message);
return mailer['verifyEmail']({
...message,
style: 'trailhead',
});
});
});
});

Просмотреть файл

@ -123,25 +123,27 @@ const LOG_METHOD_NAMES = [
const MAILER_METHOD_NAMES = [
'sendDownloadSubscription',
'sendLowRecoveryCodeNotification',
'sendNewDeviceLoginNotification',
'sendPasswordChangedNotification',
'sendPasswordResetAccountRecoveryNotification',
'sendPasswordResetNotification',
'sendPostAddAccountRecoveryNotification',
'sendPostAddTwoStepAuthNotification',
'sendPostChangePrimaryEmail',
'sendPostConsumeRecoveryCodeNotification',
'sendPostNewRecoveryCodesNotification',
'sendPostRemoveAccountRecoveryNotification',
'sendPostRemoveSecondaryEmail',
'sendPostVerifyEmail',
'sendPostRemoveTwoStepAuthNotification',
'sendPostVerifySecondaryEmail',
'sendRecoveryCode',
'sendUnblockCode',
'sendVerifyCode',
'sendVerifyLoginEmail',
'sendVerifyLoginCodeEmail',
'sendVerifySecondaryEmail',
'sendRecoveryCode',
'sendPostAddAccountRecoveryNotification',
'sendPostRemoveAccountRecoveryNotification',
'sendPasswordResetAccountRecoveryNotification',
];
const METRICS_CONTEXT_METHOD_NAMES = [

Просмотреть файл

@ -149,7 +149,7 @@ describe('remote account signin verification', function() {
.then(emailData => {
uid = emailData.headers['x-uid'];
code = emailData.headers['x-verify-code'];
assert.equal(emailData.subject, 'Confirm new sign-in to Firefox');
assert.equal(emailData.subject, 'Confirm New Sign-in');
assert.ok(uid, 'sent uid');
assert.ok(code, 'sent verify code');
@ -205,7 +205,7 @@ describe('remote account signin verification', function() {
})
.then(emailData => {
// Ensure correct email sent
assert.equal(emailData.subject, 'Verify your Firefox Account');
assert.equal(emailData.subject, 'Verify Your Account');
emailCode = emailData.headers['x-verify-code'];
assert.ok(emailCode, 'sent verify code');
return client.verifyEmail(emailCode);
@ -221,7 +221,7 @@ describe('remote account signin verification', function() {
// Verify sign-confirm email
uid = emailData.headers['x-uid'];
tokenCode = emailData.headers['x-verify-code'];
assert.equal(emailData.subject, 'Confirm new sign-in to Firefox');
assert.equal(emailData.subject, 'Confirm New Sign-in');
assert.ok(uid, 'sent uid');
assert.ok(tokenCode, 'sent verify code');
assert.notEqual(
@ -279,7 +279,7 @@ describe('remote account signin verification', function() {
assert.ok(query.code, 'code is in link');
assert.equal(query.service, options.service, 'service is in link');
assert.equal(query.resume, options.resume, 'resume is in link');
assert.equal(emailData.subject, 'Confirm new sign-in to Firefox');
assert.equal(emailData.subject, 'Confirm New Sign-in');
});
});
@ -317,11 +317,7 @@ describe('remote account signin verification', function() {
assert.ok(query.code, 'code is in link');
assert.equal(query.service, options.service, 'service is in link');
assert.equal(query.resume, options.resume, 'resume is in link');
assert.equal(
emailData.subject,
'Confirm new sign-in to Firefox',
'email subject is correct'
);
assert.equal(emailData.subject, 'Confirm New Sign-in');
})
.then(() => {
// Attempt to login from new location
@ -602,7 +598,7 @@ describe('remote account signin verification', function() {
return server.mailbox.waitForEmail(email);
})
.then(emailData => {
assert.equal(emailData.subject, 'Verify your Firefox Account');
assert.equal(emailData.subject, 'Verify Your Account');
tokenCode = emailData.headers['x-verify-code'];
assert.ok(tokenCode, 'sent verify code');
})
@ -671,7 +667,7 @@ describe('remote account signin verification', function() {
return server.mailbox.waitForEmail(email);
})
.then(emailData => {
assert.equal(emailData.subject, 'Confirm new sign-in to Firefox');
assert.equal(emailData.subject, 'Confirm New Sign-in');
tokenCode = emailData.headers['x-verify-code'];
assert.ok(tokenCode, 'sent verify code');
})

Просмотреть файл

@ -149,11 +149,7 @@ describe('/oauth/ routes', function() {
'newDeviceLoginEmail',
'correct template'
);
assert.equal(
emailData.subject,
`New sign-in to ${OAUTH_CLIENT_NAME}`,
'has client name'
);
assert.equal(emailData.subject, `New Sign-in to ${OAUTH_CLIENT_NAME}`);
assert.equal(
emailData.headers['x-service-id'],
PUBLIC_CLIENT_ID,

Просмотреть файл

@ -107,11 +107,7 @@ describe('remote password change', function() {
})
.then(emailData => {
const subject = emailData.headers['subject'];
assert.equal(
subject,
'Your Firefox Account password has been changed',
'password email subject set correctly'
);
assert.equal(subject, 'Password Changed');
const link = emailData.headers['x-link'];
const query = url.parse(link, true).query;
assert.ok(query.email, 'email is in the link');
@ -201,11 +197,7 @@ describe('remote password change', function() {
})
.then(emailData => {
const subject = emailData.headers['subject'];
assert.equal(
subject,
'Your Firefox Account password has been changed',
'password email subject set correctly'
);
assert.equal(subject, 'Password Changed');
const link = emailData.headers['x-link'];
const query = url.parse(link, true).query;
assert.ok(query.email, 'email is in the link');
@ -323,11 +315,7 @@ describe('remote password change', function() {
})
.then(emailData => {
const subject = emailData.headers['subject'];
assert.equal(
subject,
'Your Firefox Account password has been changed',
'password email subject set correctly'
);
assert.equal(subject, 'Password Changed');
const link = emailData.headers['x-link'];
const query = url.parse(link, true).query;
assert.ok(query.email, 'email is in the link');