first commit
This commit is contained in:
823
services/web/test/unit/src/Email/EmailBuilderTests.js
Normal file
823
services/web/test/unit/src/Email/EmailBuilderTests.js
Normal file
@@ -0,0 +1,823 @@
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const cheerio = require('cheerio')
|
||||
const path = require('path')
|
||||
const { expect } = require('chai')
|
||||
|
||||
const MODULE_PATH = path.join(
|
||||
__dirname,
|
||||
'../../../../app/src/Features/Email/EmailBuilder'
|
||||
)
|
||||
|
||||
const EmailMessageHelper = require('../../../../app/src/Features/Email/EmailMessageHelper')
|
||||
const ctaEmailBody = require('../../../../app/src/Features/Email/Bodies/cta-email')
|
||||
const NoCTAEmailBody = require('../../../../app/src/Features/Email/Bodies/NoCTAEmailBody')
|
||||
const BaseWithHeaderEmailLayout = require('../../../../app/src/Features/Email/Layouts/BaseWithHeaderEmailLayout')
|
||||
|
||||
describe('EmailBuilder', function () {
|
||||
before(function () {
|
||||
this.settings = {
|
||||
appName: 'testApp',
|
||||
siteUrl: 'https://www.overleaf.com',
|
||||
}
|
||||
this.EmailBuilder = SandboxedModule.require(MODULE_PATH, {
|
||||
requires: {
|
||||
'./EmailMessageHelper': EmailMessageHelper,
|
||||
'./Bodies/cta-email': ctaEmailBody,
|
||||
'./Bodies/NoCTAEmailBody': NoCTAEmailBody,
|
||||
'./Layouts/BaseWithHeaderEmailLayout': BaseWithHeaderEmailLayout,
|
||||
'@overleaf/settings': this.settings,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('projectInvite', function () {
|
||||
beforeEach(function () {
|
||||
this.opts = {
|
||||
to: 'bob@bob.com',
|
||||
first_name: 'bob',
|
||||
owner: {
|
||||
email: 'sally@hally.com',
|
||||
},
|
||||
inviteUrl: 'http://example.com/invite',
|
||||
project: {
|
||||
url: 'http://www.project.com',
|
||||
name: 'standard project',
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
describe('when sending a normal email', function () {
|
||||
beforeEach(function () {
|
||||
this.email = this.EmailBuilder.buildEmail('projectInvite', this.opts)
|
||||
})
|
||||
|
||||
it('should have html and text properties', function () {
|
||||
expect(this.email.html != null).to.equal(true)
|
||||
expect(this.email.text != null).to.equal(true)
|
||||
})
|
||||
|
||||
it('should not have undefined in it', function () {
|
||||
this.email.html.indexOf('undefined').should.equal(-1)
|
||||
this.email.subject.indexOf('undefined').should.equal(-1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when someone is up to no good', function () {
|
||||
it('should not contain the project name at all if unsafe', function () {
|
||||
this.opts.project.name = "<img src='http://evilsite.com/evil.php'>"
|
||||
this.email = this.EmailBuilder.buildEmail('projectInvite', this.opts)
|
||||
expect(this.email.html).to.not.contain('evilsite.com')
|
||||
expect(this.email.subject).to.not.contain('evilsite.com')
|
||||
|
||||
// but email should appear
|
||||
expect(this.email.html).to.contain(this.opts.owner.email)
|
||||
expect(this.email.subject).to.contain(this.opts.owner.email)
|
||||
})
|
||||
|
||||
it('should not contain the inviter email at all if unsafe', function () {
|
||||
this.opts.owner.email =
|
||||
'verylongemailaddressthatwillfailthecheck@longdomain.domain'
|
||||
this.email = this.EmailBuilder.buildEmail('projectInvite', this.opts)
|
||||
|
||||
expect(this.email.html).to.not.contain(this.opts.owner.email)
|
||||
expect(this.email.subject).to.not.contain(this.opts.owner.email)
|
||||
|
||||
// but title should appear
|
||||
expect(this.email.html).to.contain(this.opts.project.name)
|
||||
expect(this.email.subject).to.contain(this.opts.project.name)
|
||||
})
|
||||
|
||||
it('should handle both email and title being unsafe', function () {
|
||||
this.opts.project.name = "<img src='http://evilsite.com/evil.php'>"
|
||||
this.opts.owner.email =
|
||||
'verylongemailaddressthatwillfailthecheck@longdomain.domain'
|
||||
this.email = this.EmailBuilder.buildEmail('projectInvite', this.opts)
|
||||
|
||||
expect(this.email.html).to.not.contain('evilsite.com')
|
||||
expect(this.email.subject).to.not.contain('evilsite.com')
|
||||
expect(this.email.html).to.not.contain(this.opts.owner.email)
|
||||
expect(this.email.subject).to.not.contain(this.opts.owner.email)
|
||||
|
||||
expect(this.email.html).to.contain(
|
||||
'Please view the project to find out more'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('SpamSafe', function () {
|
||||
beforeEach(function () {
|
||||
this.opts = {
|
||||
to: 'bob@joe.com',
|
||||
first_name: 'bob',
|
||||
newOwner: {
|
||||
email: 'sally@hally.com',
|
||||
},
|
||||
inviteUrl: 'http://example.com/invite',
|
||||
project: {
|
||||
url: 'http://www.project.com',
|
||||
name: 'come buy my product at http://notascam.com',
|
||||
},
|
||||
}
|
||||
this.email = this.EmailBuilder.buildEmail(
|
||||
'ownershipTransferConfirmationPreviousOwner',
|
||||
this.opts
|
||||
)
|
||||
})
|
||||
|
||||
it('should replace spammy project name', function () {
|
||||
this.email.html.indexOf('your project').should.not.equal(-1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ctaTemplate', function () {
|
||||
describe('missing required content', function () {
|
||||
const content = {
|
||||
title: () => {},
|
||||
greeting: () => {},
|
||||
message: () => {},
|
||||
secondaryMessage: () => {},
|
||||
ctaText: () => {},
|
||||
ctaURL: () => {},
|
||||
gmailGoToAction: () => {},
|
||||
}
|
||||
it('should throw an error when missing title', function () {
|
||||
const { title, ...missing } = content
|
||||
expect(() => {
|
||||
this.EmailBuilder.ctaTemplate(missing)
|
||||
}).to.throw(Error)
|
||||
})
|
||||
it('should throw an error when missing message', function () {
|
||||
const { message, ...missing } = content
|
||||
expect(() => {
|
||||
this.EmailBuilder.ctaTemplate(missing)
|
||||
}).to.throw(Error)
|
||||
})
|
||||
it('should throw an error when missing ctaText', function () {
|
||||
const { ctaText, ...missing } = content
|
||||
expect(() => {
|
||||
this.EmailBuilder.ctaTemplate(missing)
|
||||
}).to.throw(Error)
|
||||
})
|
||||
it('should throw an error when missing ctaURL', function () {
|
||||
const { ctaURL, ...missing } = content
|
||||
expect(() => {
|
||||
this.EmailBuilder.ctaTemplate(missing)
|
||||
}).to.throw(Error)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('templates', function () {
|
||||
describe('CTA', function () {
|
||||
describe('canceledSubscription', function () {
|
||||
beforeEach(function () {
|
||||
this.emailAddress = 'example@overleaf.com'
|
||||
this.opts = {
|
||||
to: this.emailAddress,
|
||||
}
|
||||
this.email = this.EmailBuilder.buildEmail(
|
||||
'canceledSubscription',
|
||||
this.opts
|
||||
)
|
||||
this.expectedUrl =
|
||||
'https://docs.google.com/forms/d/e/1FAIpQLSfa7z_s-cucRRXm70N4jEcSbFsZeb0yuKThHGQL8ySEaQzF0Q/viewform?usp=sf_link'
|
||||
})
|
||||
|
||||
it('should build the email', function () {
|
||||
expect(this.email.html).to.exist
|
||||
expect(this.email.text).to.exist
|
||||
})
|
||||
|
||||
describe('HTML email', function () {
|
||||
it('should include a CTA button and a fallback CTA link', function () {
|
||||
const dom = cheerio.load(this.email.html)
|
||||
const buttonLink = dom('a:contains("Leave Feedback")')
|
||||
expect(buttonLink.length).to.equal(1)
|
||||
expect(buttonLink.attr('href')).to.equal(this.expectedUrl)
|
||||
const fallback = dom('.force-overleaf-style').last()
|
||||
expect(fallback.length).to.equal(1)
|
||||
const fallbackLink = fallback.html()
|
||||
expect(fallbackLink).to.contain(this.expectedUrl)
|
||||
})
|
||||
})
|
||||
|
||||
describe('plain text email', function () {
|
||||
it('should contain the CTA link', function () {
|
||||
expect(this.email.text).to.contain(this.expectedUrl)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('confirmEmail', function () {
|
||||
before(function () {
|
||||
this.emailAddress = 'example@overleaf.com'
|
||||
this.userId = 'abc123'
|
||||
this.opts = {
|
||||
to: this.emailAddress,
|
||||
confirmEmailUrl: `${this.settings.siteUrl}/user/emails/confirm?token=aToken123`,
|
||||
sendingUser_id: this.userId,
|
||||
}
|
||||
this.email = this.EmailBuilder.buildEmail('confirmEmail', this.opts)
|
||||
})
|
||||
|
||||
it('should build the email', function () {
|
||||
expect(this.email.html).to.exist
|
||||
expect(this.email.text).to.exist
|
||||
})
|
||||
|
||||
describe('HTML email', function () {
|
||||
it('should include a CTA button and a fallback CTA link', function () {
|
||||
const dom = cheerio.load(this.email.html)
|
||||
const buttonLink = dom('a:contains("Confirm Email")')
|
||||
expect(buttonLink.length).to.equal(1)
|
||||
expect(buttonLink.attr('href')).to.equal(this.opts.confirmEmailUrl)
|
||||
const fallback = dom('.force-overleaf-style').last()
|
||||
expect(fallback.length).to.equal(1)
|
||||
const fallbackLink = fallback.html()
|
||||
expect(fallbackLink).to.contain(this.opts.confirmEmailUrl)
|
||||
})
|
||||
})
|
||||
|
||||
describe('plain text email', function () {
|
||||
it('should contain the CTA link', function () {
|
||||
expect(this.email.text).to.contain(this.opts.confirmEmailUrl)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('ownershipTransferConfirmationNewOwner', function () {
|
||||
before(function () {
|
||||
this.emailAddress = 'example@overleaf.com'
|
||||
this.opts = {
|
||||
to: this.emailAddress,
|
||||
previousOwner: {},
|
||||
project: {
|
||||
_id: 'abc123',
|
||||
name: 'example project',
|
||||
},
|
||||
}
|
||||
this.email = this.EmailBuilder.buildEmail(
|
||||
'ownershipTransferConfirmationNewOwner',
|
||||
this.opts
|
||||
)
|
||||
this.expectedUrl = `${
|
||||
this.settings.siteUrl
|
||||
}/project/${this.opts.project._id.toString()}`
|
||||
})
|
||||
|
||||
it('should build the email', function () {
|
||||
expect(this.email.html).to.exist
|
||||
expect(this.email.text).to.exist
|
||||
})
|
||||
|
||||
describe('HTML email', function () {
|
||||
it('should include a CTA button and a fallback CTA link', function () {
|
||||
const dom = cheerio.load(this.email.html)
|
||||
const buttonLink = dom('td a')
|
||||
expect(buttonLink).to.exist
|
||||
expect(buttonLink.attr('href')).to.equal(this.expectedUrl)
|
||||
const fallback = dom('.force-overleaf-style').last()
|
||||
expect(fallback).to.exist
|
||||
const fallbackLink = fallback.html().replace(/&/g, '&')
|
||||
expect(fallbackLink).to.contain(this.expectedUrl)
|
||||
})
|
||||
})
|
||||
|
||||
describe('plain text email', function () {
|
||||
it('should contain the CTA link', function () {
|
||||
expect(this.email.text).to.contain(this.expectedUrl)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('passwordResetRequested', function () {
|
||||
before(function () {
|
||||
this.emailAddress = 'example@overleaf.com'
|
||||
this.opts = {
|
||||
to: this.emailAddress,
|
||||
setNewPasswordUrl: `${
|
||||
this.settings.siteUrl
|
||||
}/user/password/set?passwordResetToken=aToken&email=${encodeURIComponent(
|
||||
this.emailAddress
|
||||
)}`,
|
||||
}
|
||||
this.email = this.EmailBuilder.buildEmail(
|
||||
'passwordResetRequested',
|
||||
this.opts
|
||||
)
|
||||
})
|
||||
|
||||
it('should build the email', function () {
|
||||
expect(this.email.html).to.exist
|
||||
expect(this.email.text).to.exist
|
||||
})
|
||||
|
||||
describe('HTML email', function () {
|
||||
it('should include a CTA button and a fallback CTA link', function () {
|
||||
const dom = cheerio.load(this.email.html)
|
||||
const buttonLink = dom('td a')
|
||||
expect(buttonLink).to.exist
|
||||
expect(buttonLink.attr('href')).to.equal(
|
||||
this.opts.setNewPasswordUrl
|
||||
)
|
||||
const fallback = dom('.force-overleaf-style').last()
|
||||
expect(fallback).to.exist
|
||||
const fallbackLink = fallback.html().replace(/&/g, '&')
|
||||
expect(fallbackLink).to.contain(this.opts.setNewPasswordUrl)
|
||||
})
|
||||
})
|
||||
|
||||
describe('plain text email', function () {
|
||||
it('should contain the CTA link', function () {
|
||||
expect(this.email.text).to.contain(this.opts.setNewPasswordUrl)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('reconfirmEmail', function () {
|
||||
before(function () {
|
||||
this.emailAddress = 'example@overleaf.com'
|
||||
this.userId = 'abc123'
|
||||
this.opts = {
|
||||
to: this.emailAddress,
|
||||
confirmEmailUrl: `${this.settings.siteUrl}/user/emails/confirm?token=aToken123`,
|
||||
sendingUser_id: this.userId,
|
||||
}
|
||||
this.email = this.EmailBuilder.buildEmail('reconfirmEmail', this.opts)
|
||||
})
|
||||
|
||||
it('should build the email', function () {
|
||||
expect(this.email.html).to.exist
|
||||
expect(this.email.text).to.exist
|
||||
})
|
||||
|
||||
describe('HTML email', function () {
|
||||
it('should include a CTA button and a fallback CTA link', function () {
|
||||
const dom = cheerio.load(this.email.html)
|
||||
const buttonLink = dom('a:contains("Reconfirm Email")')
|
||||
expect(buttonLink.length).to.equal(1)
|
||||
expect(buttonLink.attr('href')).to.equal(this.opts.confirmEmailUrl)
|
||||
const fallback = dom('.force-overleaf-style').last()
|
||||
expect(fallback.length).to.equal(1)
|
||||
const fallbackLink = fallback.html()
|
||||
expect(fallbackLink).to.contain(this.opts.confirmEmailUrl)
|
||||
})
|
||||
})
|
||||
|
||||
describe('plain text email', function () {
|
||||
it('should contain the CTA link', function () {
|
||||
expect(this.email.text).to.contain(this.opts.confirmEmailUrl)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('verifyEmailToJoinTeam', function () {
|
||||
before(function () {
|
||||
this.emailAddress = 'example@overleaf.com'
|
||||
this.opts = {
|
||||
to: this.emailAddress,
|
||||
acceptInviteUrl: `${this.settings.siteUrl}/subscription/invites/aToken123/`,
|
||||
inviter: {
|
||||
email: 'deanna@overleaf.com',
|
||||
first_name: 'Deanna',
|
||||
last_name: 'Troi',
|
||||
},
|
||||
}
|
||||
this.email = this.EmailBuilder.buildEmail(
|
||||
'verifyEmailToJoinTeam',
|
||||
this.opts
|
||||
)
|
||||
})
|
||||
|
||||
it('should build the email', function () {
|
||||
expect(this.email.html).to.exist
|
||||
expect(this.email.text).to.exist
|
||||
})
|
||||
|
||||
describe('HTML email', function () {
|
||||
it('should include a CTA button and a fallback CTA link', function () {
|
||||
const dom = cheerio.load(this.email.html)
|
||||
const buttonLink = dom('a:contains("Join now")')
|
||||
expect(buttonLink.length).to.equal(1)
|
||||
expect(buttonLink.attr('href')).to.equal(this.opts.acceptInviteUrl)
|
||||
const fallback = dom('.force-overleaf-style').last()
|
||||
expect(fallback.length).to.equal(1)
|
||||
const fallbackLink = fallback.html()
|
||||
expect(fallbackLink).to.contain(this.opts.acceptInviteUrl)
|
||||
})
|
||||
})
|
||||
|
||||
describe('plain text email', function () {
|
||||
it('should contain the CTA link', function () {
|
||||
expect(this.email.text).to.contain(this.opts.acceptInviteUrl)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('reactivatedSubscription', function () {
|
||||
before(function () {
|
||||
this.emailAddress = 'example@overleaf.com'
|
||||
this.opts = {
|
||||
to: this.emailAddress,
|
||||
}
|
||||
this.email = this.EmailBuilder.buildEmail(
|
||||
'reactivatedSubscription',
|
||||
this.opts
|
||||
)
|
||||
this.expectedUrl = `${this.settings.siteUrl}/user/subscription`
|
||||
})
|
||||
|
||||
it('should build the email', function () {
|
||||
expect(this.email.html).to.exist
|
||||
expect(this.email.text).to.exist
|
||||
})
|
||||
|
||||
describe('HTML email', function () {
|
||||
it('should include a CTA button and a fallback CTA link', function () {
|
||||
const dom = cheerio.load(this.email.html)
|
||||
const buttonLink = dom('a:contains("View Subscription Dashboard")')
|
||||
expect(buttonLink.length).to.equal(1)
|
||||
expect(buttonLink.attr('href')).to.equal(this.expectedUrl)
|
||||
const fallback = dom('.force-overleaf-style').last()
|
||||
expect(fallback.length).to.equal(1)
|
||||
const fallbackLink = fallback.html()
|
||||
expect(fallbackLink).to.contain(this.expectedUrl)
|
||||
})
|
||||
})
|
||||
|
||||
describe('plain text email', function () {
|
||||
it('should contain the CTA link', function () {
|
||||
expect(this.email.text).to.contain(this.expectedUrl)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('testEmail', function () {
|
||||
before(function () {
|
||||
this.emailAddress = 'example@overleaf.com'
|
||||
this.opts = {
|
||||
to: this.emailAddress,
|
||||
}
|
||||
this.email = this.EmailBuilder.buildEmail('testEmail', this.opts)
|
||||
})
|
||||
|
||||
it('should build the email', function () {
|
||||
expect(this.email.html).to.exist
|
||||
expect(this.email.text).to.exist
|
||||
})
|
||||
|
||||
describe('HTML email', function () {
|
||||
it('should include a CTA button and a fallback CTA link', function () {
|
||||
const dom = cheerio.load(this.email.html)
|
||||
const buttonLink = dom(
|
||||
`a:contains("Open ${this.settings.appName}")`
|
||||
)
|
||||
expect(buttonLink.length).to.equal(1)
|
||||
expect(buttonLink.attr('href')).to.equal(this.settings.siteUrl)
|
||||
const fallback = dom('.force-overleaf-style').last()
|
||||
expect(fallback.length).to.equal(1)
|
||||
const fallbackLink = fallback.html()
|
||||
expect(fallbackLink).to.contain(this.settings.siteUrl)
|
||||
})
|
||||
})
|
||||
|
||||
describe('plain text email', function () {
|
||||
it('should contain the CTA link', function () {
|
||||
expect(this.email.text).to.contain(
|
||||
`Open ${this.settings.appName}: ${this.settings.siteUrl}`
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('registered', function () {
|
||||
before(function () {
|
||||
this.emailAddress = 'example@overleaf.com'
|
||||
this.opts = {
|
||||
to: this.emailAddress,
|
||||
setNewPasswordUrl: `${this.settings.siteUrl}/user/activate?token=aToken123&user_id=aUserId123`,
|
||||
}
|
||||
this.email = this.EmailBuilder.buildEmail('registered', this.opts)
|
||||
})
|
||||
|
||||
it('should build the email', function () {
|
||||
expect(this.email.html).to.exist
|
||||
expect(this.email.text).to.exist
|
||||
})
|
||||
|
||||
describe('HTML email', function () {
|
||||
it('should include a CTA button and a fallback CTA link', function () {
|
||||
const dom = cheerio.load(this.email.html)
|
||||
const buttonLink = dom('a:contains("Set password")')
|
||||
expect(buttonLink.length).to.equal(1)
|
||||
expect(buttonLink.attr('href')).to.equal(
|
||||
this.opts.setNewPasswordUrl
|
||||
)
|
||||
const fallback = dom('.force-overleaf-style').last()
|
||||
expect(fallback.length).to.equal(1)
|
||||
const fallbackLink = fallback.html().replace(/&/, '&')
|
||||
expect(fallbackLink).to.contain(this.opts.setNewPasswordUrl)
|
||||
})
|
||||
})
|
||||
|
||||
describe('plain text email', function () {
|
||||
it('should contain the CTA link', function () {
|
||||
expect(this.email.text).to.contain(this.opts.setNewPasswordUrl)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('projectInvite', function () {
|
||||
before(function () {
|
||||
this.emailAddress = 'example@overleaf.com'
|
||||
this.owner = {
|
||||
email: 'owner@example.com',
|
||||
name: 'Bailey',
|
||||
}
|
||||
this.projectName = 'Top Secret'
|
||||
this.opts = {
|
||||
inviteUrl: `${this.settings.siteUrl}/project/projectId123/invite/token/aToken123`,
|
||||
owner: {
|
||||
email: this.owner.email,
|
||||
},
|
||||
project: {
|
||||
name: this.projectName,
|
||||
},
|
||||
to: this.emailAddress,
|
||||
}
|
||||
this.email = this.EmailBuilder.buildEmail('projectInvite', this.opts)
|
||||
})
|
||||
|
||||
it('should build the email', function () {
|
||||
expect(this.email.html).to.exist
|
||||
expect(this.email.text).to.exist
|
||||
})
|
||||
|
||||
describe('HTML email', function () {
|
||||
it('should include a CTA button and a fallback CTA link', function () {
|
||||
const dom = cheerio.load(this.email.html)
|
||||
const buttonLink = dom('a:contains("View project")')
|
||||
expect(buttonLink.length).to.equal(1)
|
||||
expect(buttonLink.attr('href')).to.equal(this.opts.inviteUrl)
|
||||
const fallback = dom('.force-overleaf-style').last()
|
||||
expect(fallback.length).to.equal(1)
|
||||
const fallbackLink = fallback.html().replace(/&/g, '&')
|
||||
expect(fallbackLink).to.contain(this.opts.inviteUrl)
|
||||
})
|
||||
})
|
||||
|
||||
describe('plain text email', function () {
|
||||
it('should contain the CTA link', function () {
|
||||
expect(this.email.text).to.contain(this.opts.inviteUrl)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('welcome', function () {
|
||||
beforeEach(function () {
|
||||
this.emailAddress = 'example@overleaf.com'
|
||||
this.opts = {
|
||||
to: this.emailAddress,
|
||||
confirmEmailUrl: `${this.settings.siteUrl}/user/emails/confirm?token=token123`,
|
||||
}
|
||||
this.email = this.EmailBuilder.buildEmail('welcome', this.opts)
|
||||
this.dom = cheerio.load(this.email.html)
|
||||
})
|
||||
|
||||
it('should build the email', function () {
|
||||
expect(this.email.html).to.exist
|
||||
expect(this.email.text).to.exist
|
||||
})
|
||||
|
||||
describe('HTML email', function () {
|
||||
it('should include a CTA button and a fallback CTA link', function () {
|
||||
const buttonLink = this.dom('a:contains("Confirm Email")')
|
||||
expect(buttonLink.length).to.equal(1)
|
||||
expect(buttonLink.attr('href')).to.equal(this.opts.confirmEmailUrl)
|
||||
const fallback = this.dom('.force-overleaf-style').last()
|
||||
expect(fallback.length).to.equal(1)
|
||||
expect(fallback.html()).to.contain(this.opts.confirmEmailUrl)
|
||||
})
|
||||
it('should include help links', function () {
|
||||
const helpGuidesLink = this.dom('a:contains("Help Guides")')
|
||||
const templatesLink = this.dom('a:contains("Templates")')
|
||||
const logInLink = this.dom('a:contains("log in")')
|
||||
expect(helpGuidesLink.length).to.equal(1)
|
||||
expect(templatesLink.length).to.equal(1)
|
||||
expect(logInLink.length).to.equal(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('plain text email', function () {
|
||||
it('should contain the CTA URL', function () {
|
||||
expect(this.email.text).to.contain(this.opts.confirmEmailUrl)
|
||||
})
|
||||
it('should include help URL', function () {
|
||||
expect(this.email.text).to.contain('/learn')
|
||||
expect(this.email.text).to.contain('/login')
|
||||
expect(this.email.text).to.contain('/templates')
|
||||
})
|
||||
it('should contain HTML links', function () {
|
||||
expect(this.email.text).to.not.contain('<a')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('groupSSODisabled', function () {
|
||||
it('should build the email for non managed and linked users', function () {
|
||||
const setNewPasswordUrl = `${this.settings.siteUrl}/user/password/reset`
|
||||
const emailAddress = 'example@overleaf.com'
|
||||
const opts = {
|
||||
to: emailAddress,
|
||||
setNewPasswordUrl,
|
||||
userIsManaged: false,
|
||||
}
|
||||
const email = this.EmailBuilder.buildEmail('groupSSODisabled', opts)
|
||||
expect(email.subject).to.equal(
|
||||
'A change to your Overleaf login options'
|
||||
)
|
||||
const dom = cheerio.load(email.html)
|
||||
expect(email.html).to.exist
|
||||
expect(email.html).to.contain(
|
||||
'Your group administrator has disabled single sign-on for your group.'
|
||||
)
|
||||
expect(email.html).to.contain(
|
||||
'You can still log in to Overleaf using one of our other'
|
||||
)
|
||||
const links = dom('a')
|
||||
expect(links[0].attribs.href).to.equal(
|
||||
`${this.settings.siteUrl}/login`
|
||||
)
|
||||
expect(links[1].attribs.href).to.equal(setNewPasswordUrl)
|
||||
expect(email.html).to.contain(
|
||||
"If you don't have a password, you can set one now."
|
||||
)
|
||||
expect(email.text).to.exist
|
||||
const expectedPlainText = [
|
||||
'Hi,',
|
||||
'',
|
||||
'Your group administrator has disabled single sign-on for your group.',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'What does this mean for you?',
|
||||
'',
|
||||
'You can still log in to Overleaf using one of our other login options or with your email address and password.',
|
||||
'',
|
||||
"If you don't have a password, you can set one now.",
|
||||
'',
|
||||
`Set your new password: ${setNewPasswordUrl}`,
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'Regards,',
|
||||
`The ${this.settings.appName} Team - ${this.settings.siteUrl}`,
|
||||
]
|
||||
expect(email.text.split(/\r?\n/)).to.deep.equal(expectedPlainText)
|
||||
})
|
||||
|
||||
it('should build the email for managed and linked users', function () {
|
||||
const emailAddress = 'example@overleaf.com'
|
||||
const setNewPasswordUrl = `${this.settings.siteUrl}/user/password/reset`
|
||||
const opts = {
|
||||
to: emailAddress,
|
||||
setNewPasswordUrl,
|
||||
userIsManaged: true,
|
||||
}
|
||||
const email = this.EmailBuilder.buildEmail('groupSSODisabled', opts)
|
||||
expect(email.subject).to.equal(
|
||||
'Action required: Set your Overleaf password'
|
||||
)
|
||||
const dom = cheerio.load(email.html)
|
||||
expect(email.html).to.exist
|
||||
expect(email.html).to.contain(
|
||||
'Your group administrator has disabled single sign-on for your group.'
|
||||
)
|
||||
expect(email.html).to.contain(
|
||||
'You now need an email address and password to sign in to your Overleaf account.'
|
||||
)
|
||||
const links = dom('a')
|
||||
expect(links[0].attribs.href).to.equal(
|
||||
`${this.settings.siteUrl}/user/password/reset`
|
||||
)
|
||||
|
||||
expect(email.text).to.exist
|
||||
|
||||
const expectedPlainText = [
|
||||
'Hi,',
|
||||
'',
|
||||
'Your group administrator has disabled single sign-on for your group.',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'What does this mean for you?',
|
||||
'',
|
||||
'You now need an email address and password to sign in to your Overleaf account.',
|
||||
'',
|
||||
`Set your new password: ${setNewPasswordUrl}`,
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'Regards,',
|
||||
`The ${this.settings.appName} Team - ${this.settings.siteUrl}`,
|
||||
]
|
||||
|
||||
expect(email.text.split(/\r?\n/)).to.deep.equal(expectedPlainText)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('no CTA', function () {
|
||||
describe('securityAlert', function () {
|
||||
before(function () {
|
||||
this.message = 'more details about the action'
|
||||
this.messageHTML = `<br /><span style="text-align:center" class="a-class"><b><i>${this.message}</i></b></span>`
|
||||
this.messageNotAllowedHTML = `<div></div>${this.messageHTML}`
|
||||
|
||||
this.actionDescribed = 'an action described'
|
||||
this.actionDescribedHTML = `<br /><span style="text-align:center" class="a-class"><b><i>${this.actionDescribed}</i></b>`
|
||||
this.actionDescribedNotAllowedHTML = `<div></div>${this.actionDescribedHTML}`
|
||||
|
||||
this.opts = {
|
||||
to: this.email,
|
||||
actionDescribed: this.actionDescribedNotAllowedHTML,
|
||||
action: 'an action',
|
||||
message: [this.messageNotAllowedHTML],
|
||||
}
|
||||
this.email = this.EmailBuilder.buildEmail('securityAlert', this.opts)
|
||||
})
|
||||
|
||||
it('should build the email', function () {
|
||||
expect(this.email.html != null).to.equal(true)
|
||||
expect(this.email.text != null).to.equal(true)
|
||||
})
|
||||
|
||||
describe('HTML email', function () {
|
||||
it('should clean HTML in opts.actionDescribed', function () {
|
||||
expect(this.email.html).to.not.contain(
|
||||
this.actionDescribedNotAllowedHTML
|
||||
)
|
||||
expect(this.email.html).to.contain(this.actionDescribedHTML)
|
||||
})
|
||||
it('should clean HTML in opts.message', function () {
|
||||
expect(this.email.html).to.not.contain(this.messageNotAllowedHTML)
|
||||
expect(this.email.html).to.contain(this.messageHTML)
|
||||
})
|
||||
})
|
||||
|
||||
describe('plain text email', function () {
|
||||
it('should remove all HTML in opts.actionDescribed', function () {
|
||||
expect(this.email.text).to.not.contain(this.actionDescribedHTML)
|
||||
expect(this.email.text).to.contain(this.actionDescribed)
|
||||
})
|
||||
it('should remove all HTML in opts.message', function () {
|
||||
expect(this.email.text).to.not.contain(this.messageHTML)
|
||||
expect(this.email.text).to.contain(this.message)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('welcomeWithoutCTA', function () {
|
||||
beforeEach(function () {
|
||||
this.emailAddress = 'example@overleaf.com'
|
||||
this.opts = {
|
||||
to: this.emailAddress,
|
||||
}
|
||||
this.email = this.EmailBuilder.buildEmail(
|
||||
'welcomeWithoutCTA',
|
||||
this.opts
|
||||
)
|
||||
this.dom = cheerio.load(this.email.html)
|
||||
})
|
||||
|
||||
it('should build the email', function () {
|
||||
expect(this.email.html).to.exist
|
||||
expect(this.email.text).to.exist
|
||||
})
|
||||
|
||||
describe('HTML email', function () {
|
||||
it('should include help links', function () {
|
||||
const helpGuidesLink = this.dom('a:contains("Help Guides")')
|
||||
const templatesLink = this.dom('a:contains("Templates")')
|
||||
const logInLink = this.dom('a:contains("log in")')
|
||||
expect(helpGuidesLink.length).to.equal(1)
|
||||
expect(templatesLink.length).to.equal(1)
|
||||
expect(logInLink.length).to.equal(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('plain text email', function () {
|
||||
it('should include help URL', function () {
|
||||
expect(this.email.text).to.contain('/learn')
|
||||
expect(this.email.text).to.contain('/login')
|
||||
expect(this.email.text).to.contain('/templates')
|
||||
})
|
||||
it('should contain HTML links', function () {
|
||||
expect(this.email.text).to.not.contain('<a')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
122
services/web/test/unit/src/Email/EmailHandlerTests.js
Normal file
122
services/web/test/unit/src/Email/EmailHandlerTests.js
Normal file
@@ -0,0 +1,122 @@
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const path = require('path')
|
||||
const sinon = require('sinon')
|
||||
const { expect } = require('chai')
|
||||
|
||||
const MODULE_PATH = path.join(
|
||||
__dirname,
|
||||
'../../../../app/src/Features/Email/EmailHandler'
|
||||
)
|
||||
|
||||
describe('EmailHandler', function () {
|
||||
beforeEach(function () {
|
||||
this.html = '<html>hello</html>'
|
||||
this.Settings = { email: {} }
|
||||
this.EmailBuilder = {
|
||||
buildEmail: sinon.stub().returns({ html: this.html }),
|
||||
}
|
||||
this.EmailSender = {
|
||||
promises: {
|
||||
sendEmail: sinon.stub().resolves(),
|
||||
},
|
||||
}
|
||||
this.Queues = {
|
||||
createScheduledJob: sinon.stub().resolves(),
|
||||
}
|
||||
this.EmailHandler = SandboxedModule.require(MODULE_PATH, {
|
||||
requires: {
|
||||
'./EmailBuilder': this.EmailBuilder,
|
||||
'./EmailSender': this.EmailSender,
|
||||
'@overleaf/settings': this.Settings,
|
||||
'../../infrastructure/Queues': this.Queues,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('send email', function () {
|
||||
it('should use the correct options', async function () {
|
||||
const opts = { to: 'bob@bob.com' }
|
||||
await this.EmailHandler.promises.sendEmail('welcome', opts)
|
||||
expect(this.EmailSender.promises.sendEmail).to.have.been.calledWithMatch({
|
||||
html: this.html,
|
||||
})
|
||||
})
|
||||
|
||||
it('should return the error', async function () {
|
||||
this.EmailSender.promises.sendEmail.rejects(new Error('boom'))
|
||||
const opts = {
|
||||
to: 'bob@bob.com',
|
||||
subject: 'hello bob',
|
||||
}
|
||||
await expect(this.EmailHandler.promises.sendEmail('welcome', opts)).to.be
|
||||
.rejected
|
||||
})
|
||||
|
||||
it('should not send an email if lifecycle is not enabled', async function () {
|
||||
this.Settings.email.lifecycle = false
|
||||
this.EmailBuilder.buildEmail.returns({ type: 'lifecycle' })
|
||||
await this.EmailHandler.promises.sendEmail('welcome', {})
|
||||
expect(this.EmailSender.promises.sendEmail).not.to.have.been.called
|
||||
})
|
||||
|
||||
it('should send an email if lifecycle is not enabled but the type is notification', async function () {
|
||||
this.Settings.email.lifecycle = false
|
||||
this.EmailBuilder.buildEmail.returns({ type: 'notification' })
|
||||
const opts = { to: 'bob@bob.com' }
|
||||
await this.EmailHandler.promises.sendEmail('welcome', opts)
|
||||
expect(this.EmailSender.promises.sendEmail).to.have.been.called
|
||||
})
|
||||
|
||||
it('should send lifecycle email if it is enabled', async function () {
|
||||
this.Settings.email.lifecycle = true
|
||||
this.EmailBuilder.buildEmail.returns({ type: 'lifecycle' })
|
||||
const opts = { to: 'bob@bob.com' }
|
||||
await this.EmailHandler.promises.sendEmail('welcome', opts)
|
||||
expect(this.EmailSender.promises.sendEmail).to.have.been.called
|
||||
})
|
||||
|
||||
describe('with plain-text email content', function () {
|
||||
beforeEach(function () {
|
||||
this.text = 'hello there'
|
||||
})
|
||||
|
||||
it('should pass along the text field', async function () {
|
||||
this.EmailBuilder.buildEmail.returns({
|
||||
html: this.html,
|
||||
text: this.text,
|
||||
})
|
||||
const opts = { to: 'bob@bob.com' }
|
||||
await this.EmailHandler.promises.sendEmail('welcome', opts)
|
||||
expect(
|
||||
this.EmailSender.promises.sendEmail
|
||||
).to.have.been.calledWithMatch({
|
||||
html: this.html,
|
||||
text: this.text,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('send deferred email', function () {
|
||||
beforeEach(function () {
|
||||
this.opts = {
|
||||
to: 'bob@bob.com',
|
||||
first_name: 'hello bob',
|
||||
}
|
||||
this.emailType = 'canceledSubscription'
|
||||
this.ONE_HOUR_IN_MS = 1000 * 60 * 60
|
||||
this.EmailHandler.sendDeferredEmail(
|
||||
this.emailType,
|
||||
this.opts,
|
||||
this.ONE_HOUR_IN_MS
|
||||
)
|
||||
})
|
||||
it('should add a email job to the queue', function () {
|
||||
expect(this.Queues.createScheduledJob).to.have.been.calledWith(
|
||||
'deferred-emails',
|
||||
{ data: { emailType: this.emailType, opts: this.opts } },
|
||||
this.ONE_HOUR_IN_MS
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
35
services/web/test/unit/src/Email/EmailMessageHelperTests.js
Normal file
35
services/web/test/unit/src/Email/EmailMessageHelperTests.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const path = require('path')
|
||||
const { expect } = require('chai')
|
||||
|
||||
const MODULE_PATH = path.join(
|
||||
__dirname,
|
||||
'../../../../app/src/Features/Email/EmailMessageHelper'
|
||||
)
|
||||
|
||||
describe('EmailMessageHelper', function () {
|
||||
beforeEach(function () {
|
||||
this.EmailMessageHelper = SandboxedModule.require(MODULE_PATH, {})
|
||||
})
|
||||
describe('cleanHTML', function () {
|
||||
beforeEach(function () {
|
||||
this.text = 'a message'
|
||||
this.span = `<span style="text-align:center">${this.text}</span>`
|
||||
this.fullMessage = `${this.span}<div></div>`
|
||||
})
|
||||
it('should remove HTML for plainText version', function () {
|
||||
const processed = this.EmailMessageHelper.cleanHTML(
|
||||
this.fullMessage,
|
||||
true
|
||||
)
|
||||
expect(processed).to.equal(this.text)
|
||||
})
|
||||
it('should keep HTML for HTML version but remove tags not allowed', function () {
|
||||
const processed = this.EmailMessageHelper.cleanHTML(
|
||||
this.fullMessage,
|
||||
false
|
||||
)
|
||||
expect(processed).to.equal(this.span)
|
||||
})
|
||||
})
|
||||
})
|
||||
131
services/web/test/unit/src/Email/EmailSenderTests.js
Normal file
131
services/web/test/unit/src/Email/EmailSenderTests.js
Normal file
@@ -0,0 +1,131 @@
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const path = require('path')
|
||||
const sinon = require('sinon')
|
||||
const { expect } = require('chai')
|
||||
|
||||
const MODULE_PATH = path.join(
|
||||
__dirname,
|
||||
'../../../../app/src/Features/Email/EmailSender.js'
|
||||
)
|
||||
|
||||
describe('EmailSender', function () {
|
||||
beforeEach(function () {
|
||||
this.rateLimiter = {
|
||||
consume: sinon.stub().resolves(),
|
||||
}
|
||||
this.RateLimiter = {
|
||||
RateLimiter: sinon.stub().returns(this.rateLimiter),
|
||||
}
|
||||
|
||||
this.Settings = {
|
||||
email: {
|
||||
transport: 'ses',
|
||||
parameters: {
|
||||
AWSAccessKeyID: 'key',
|
||||
AWSSecretKey: 'secret',
|
||||
},
|
||||
fromAddress: 'bob@bob.com',
|
||||
replyToAddress: 'sally@gmail.com',
|
||||
},
|
||||
}
|
||||
|
||||
this.sesClient = { sendMail: sinon.stub().resolves() }
|
||||
|
||||
this.ses = { createTransport: () => this.sesClient }
|
||||
|
||||
this.EmailSender = SandboxedModule.require(MODULE_PATH, {
|
||||
requires: {
|
||||
nodemailer: this.ses,
|
||||
'nodemailer-ses-transport': sinon.stub(),
|
||||
'@overleaf/settings': this.Settings,
|
||||
'../../infrastructure/RateLimiter': this.RateLimiter,
|
||||
'@overleaf/metrics': {
|
||||
inc() {},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
this.opts = {
|
||||
to: 'bob@bob.com',
|
||||
subject: 'new email',
|
||||
html: '<hello></hello>',
|
||||
}
|
||||
})
|
||||
|
||||
describe('sendEmail', function () {
|
||||
it('should set the properties on the email to send', async function () {
|
||||
await this.EmailSender.promises.sendEmail(this.opts)
|
||||
expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
|
||||
html: this.opts.html,
|
||||
to: this.opts.to,
|
||||
subject: this.opts.subject,
|
||||
})
|
||||
})
|
||||
|
||||
it('should return a non-specific error', async function () {
|
||||
this.sesClient.sendMail.rejects(new Error('boom'))
|
||||
await expect(this.EmailSender.promises.sendEmail({})).to.be.rejectedWith(
|
||||
'error sending message'
|
||||
)
|
||||
})
|
||||
|
||||
it('should use the from address from settings', async function () {
|
||||
await this.EmailSender.promises.sendEmail(this.opts)
|
||||
expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
|
||||
from: this.Settings.email.fromAddress,
|
||||
})
|
||||
})
|
||||
|
||||
it('should use the reply to address from settings', async function () {
|
||||
await this.EmailSender.promises.sendEmail(this.opts)
|
||||
expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
|
||||
replyTo: this.Settings.email.replyToAddress,
|
||||
})
|
||||
})
|
||||
|
||||
it('should use the reply to address in options as an override', async function () {
|
||||
this.opts.replyTo = 'someone@else.com'
|
||||
await this.EmailSender.promises.sendEmail(this.opts)
|
||||
expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
|
||||
replyTo: this.opts.replyTo,
|
||||
})
|
||||
})
|
||||
|
||||
it('should not send an email when the rate limiter says no', async function () {
|
||||
this.opts.sendingUser_id = '12321312321'
|
||||
this.rateLimiter.consume.rejects({ remainingPoints: 0 })
|
||||
await expect(this.EmailSender.promises.sendEmail(this.opts)).to.be
|
||||
.rejected
|
||||
expect(this.sesClient.sendMail).not.to.have.been.called
|
||||
})
|
||||
|
||||
it('should send the email when the rate limtier says continue', async function () {
|
||||
this.opts.sendingUser_id = '12321312321'
|
||||
await this.EmailSender.promises.sendEmail(this.opts)
|
||||
expect(this.sesClient.sendMail).to.have.been.called
|
||||
})
|
||||
|
||||
it('should not check the rate limiter when there is no sendingUser_id', async function () {
|
||||
this.EmailSender.sendEmail(this.opts, () => {
|
||||
expect(this.sesClient.sendMail).to.have.been.called
|
||||
expect(this.rateLimiter.consume).not.to.have.been.called
|
||||
})
|
||||
})
|
||||
|
||||
describe('with plain-text email content', function () {
|
||||
beforeEach(function () {
|
||||
this.opts.text = 'hello there'
|
||||
})
|
||||
|
||||
it('should set the text property on the email to send', async function () {
|
||||
await this.EmailSender.promises.sendEmail(this.opts)
|
||||
expect(this.sesClient.sendMail).to.have.been.calledWithMatch({
|
||||
html: this.opts.html,
|
||||
text: this.opts.text,
|
||||
to: this.opts.to,
|
||||
subject: this.opts.subject,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
76
services/web/test/unit/src/Email/SpamSafeTests.js
Normal file
76
services/web/test/unit/src/Email/SpamSafeTests.js
Normal file
@@ -0,0 +1,76 @@
|
||||
const path = require('path')
|
||||
const modulePath = path.join(
|
||||
__dirname,
|
||||
'../../../../app/src/Features/Email/SpamSafe'
|
||||
)
|
||||
const SpamSafe = require(modulePath)
|
||||
const { expect } = require('chai')
|
||||
|
||||
describe('SpamSafe', function () {
|
||||
it('should reject spammy names', function () {
|
||||
expect(SpamSafe.isSafeUserName('Charline Wałęsa')).to.equal(true)
|
||||
expect(
|
||||
SpamSafe.isSafeUserName(
|
||||
"hey come buy this product im selling it's really good for you and it'll make your latex 10x guaranteed"
|
||||
)
|
||||
).to.equal(false)
|
||||
expect(SpamSafe.isSafeUserName('隆太郎 宮本')).to.equal(true)
|
||||
expect(SpamSafe.isSafeUserName('Visit haxx0red.com')).to.equal(false)
|
||||
expect(
|
||||
SpamSafe.isSafeUserName(
|
||||
'加美汝VX:hihi661,金沙2001005com the first deposit will be _100%_'
|
||||
)
|
||||
).to.equal(false)
|
||||
expect(
|
||||
SpamSafe.isSafeProjectName(
|
||||
'Neural Networks: good for your health and will solve all your problems'
|
||||
)
|
||||
).to.equal(false)
|
||||
expect(
|
||||
SpamSafe.isSafeProjectName(
|
||||
'An analysis of the questions of the universe!'
|
||||
)
|
||||
).to.equal(true)
|
||||
expect(SpamSafe.isSafeProjectName("A'p'o's't'r'o'p'h'e gallore")).to.equal(
|
||||
true
|
||||
)
|
||||
expect(
|
||||
SpamSafe.isSafeProjectName(
|
||||
'come buy this => http://www.dopeproduct.com/search/?q=user123'
|
||||
)
|
||||
).to.equal(false)
|
||||
expect(
|
||||
SpamSafe.isSafeEmail('realistic-email+1@domain.sub-hyphen.com')
|
||||
).to.equal(true)
|
||||
expect(SpamSafe.isSafeEmail('notquiteRight@evil$.com')).to.equal(false)
|
||||
|
||||
expect(SpamSafe.safeUserName('Tammy Weinstįen', 'A User')).to.equal(
|
||||
'Tammy Weinstįen'
|
||||
)
|
||||
expect(SpamSafe.safeUserName('haxx0red.com', 'A User')).to.equal('A User')
|
||||
expect(SpamSafe.safeUserName('What$ Upp', 'A User')).to.equal('A User')
|
||||
expect(SpamSafe.safeProjectName('Math-ematics!', 'A Project')).to.equal(
|
||||
'Math-ematics!'
|
||||
)
|
||||
expect(
|
||||
SpamSafe.safeProjectName(
|
||||
`A Very long title for a very long book that will never be read${'a'.repeat(
|
||||
250
|
||||
)}`,
|
||||
'A Project'
|
||||
)
|
||||
).to.equal('A Project')
|
||||
expect(
|
||||
SpamSafe.safeEmail('safe-ëmail@domain.com', 'A collaborator')
|
||||
).to.equal('safe-ëmail@domain.com')
|
||||
expect(
|
||||
SpamSafe.safeEmail('Բարեւ@another.domain', 'A collaborator')
|
||||
).to.equal('Բարեւ@another.domain')
|
||||
expect(
|
||||
SpamSafe.safeEmail(`me+${'a'.repeat(40)}@googoole.con`, 'A collaborator')
|
||||
).to.equal('A collaborator')
|
||||
expect(
|
||||
SpamSafe.safeEmail('sendME$$$@iAmAprince.com', 'A collaborator')
|
||||
).to.equal('A collaborator')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user