EmailHandler.test.mjs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import { vi, expect } from 'vitest'
  2. import path from 'node:path'
  3. import sinon from 'sinon'
  4. const MODULE_PATH = path.join(
  5. import.meta.dirname,
  6. '../../../../app/src/Features/Email/EmailHandler'
  7. )
  8. describe('EmailHandler', function () {
  9. beforeEach(async function (ctx) {
  10. ctx.html = '<html>hello</html>'
  11. ctx.Settings = { email: {} }
  12. ctx.EmailBuilder = {
  13. buildEmail: sinon.stub().returns({ html: ctx.html }),
  14. }
  15. ctx.EmailSender = {
  16. promises: {
  17. sendEmail: sinon.stub().resolves(),
  18. },
  19. }
  20. ctx.Queues = {
  21. createScheduledJob: sinon.stub().resolves(),
  22. }
  23. vi.doMock('../../../../app/src/Features/Email/EmailBuilder', () => ({
  24. default: ctx.EmailBuilder,
  25. }))
  26. vi.doMock('../../../../app/src/Features/Email/EmailSender', () => ({
  27. default: ctx.EmailSender,
  28. }))
  29. vi.doMock('@overleaf/settings', () => ({
  30. default: ctx.Settings,
  31. }))
  32. vi.doMock('../../../../app/src/infrastructure/Queues', () => ({
  33. default: ctx.Queues,
  34. }))
  35. ctx.EmailHandler = (await import(MODULE_PATH)).default
  36. })
  37. describe('send email', function () {
  38. it('should use the correct options', async function (ctx) {
  39. const opts = { to: 'bob@bob.com' }
  40. await ctx.EmailHandler.promises.sendEmail('welcome', opts)
  41. expect(ctx.EmailSender.promises.sendEmail).to.have.been.calledWithMatch({
  42. html: ctx.html,
  43. })
  44. })
  45. it('should return the error', async function (ctx) {
  46. ctx.EmailSender.promises.sendEmail.rejects(new Error('boom'))
  47. const opts = {
  48. to: 'bob@bob.com',
  49. subject: 'hello bob',
  50. }
  51. await expect(ctx.EmailHandler.promises.sendEmail('welcome', opts)).to.be
  52. .rejected
  53. })
  54. it('should not send an email if lifecycle is not enabled', async function (ctx) {
  55. ctx.Settings.email.lifecycle = false
  56. ctx.EmailBuilder.buildEmail.returns({ type: 'lifecycle' })
  57. await ctx.EmailHandler.promises.sendEmail('welcome', {})
  58. expect(ctx.EmailSender.promises.sendEmail).not.to.have.been.called
  59. })
  60. it('should send an email if lifecycle is not enabled but the type is notification', async function (ctx) {
  61. ctx.Settings.email.lifecycle = false
  62. ctx.EmailBuilder.buildEmail.returns({ type: 'notification' })
  63. const opts = { to: 'bob@bob.com' }
  64. await ctx.EmailHandler.promises.sendEmail('welcome', opts)
  65. expect(ctx.EmailSender.promises.sendEmail).to.have.been.called
  66. })
  67. it('should send lifecycle email if it is enabled', async function (ctx) {
  68. ctx.Settings.email.lifecycle = true
  69. ctx.EmailBuilder.buildEmail.returns({ type: 'lifecycle' })
  70. const opts = { to: 'bob@bob.com' }
  71. await ctx.EmailHandler.promises.sendEmail('welcome', opts)
  72. expect(ctx.EmailSender.promises.sendEmail).to.have.been.called
  73. })
  74. describe('with plain-text email content', function () {
  75. beforeEach(function (ctx) {
  76. ctx.text = 'hello there'
  77. })
  78. it('should pass along the text field', async function (ctx) {
  79. ctx.EmailBuilder.buildEmail.returns({
  80. html: ctx.html,
  81. text: ctx.text,
  82. })
  83. const opts = { to: 'bob@bob.com' }
  84. await ctx.EmailHandler.promises.sendEmail('welcome', opts)
  85. expect(ctx.EmailSender.promises.sendEmail).to.have.been.calledWithMatch(
  86. {
  87. html: ctx.html,
  88. text: ctx.text,
  89. }
  90. )
  91. })
  92. })
  93. })
  94. describe('send deferred email', function () {
  95. beforeEach(function (ctx) {
  96. ctx.opts = {
  97. to: 'bob@bob.com',
  98. first_name: 'hello bob',
  99. }
  100. ctx.emailType = 'canceledSubscription'
  101. ctx.ONE_HOUR_IN_MS = 1000 * 60 * 60
  102. ctx.EmailHandler.sendDeferredEmail(
  103. ctx.emailType,
  104. ctx.opts,
  105. ctx.ONE_HOUR_IN_MS
  106. )
  107. })
  108. it('should add a email job to the queue', function (ctx) {
  109. expect(ctx.Queues.createScheduledJob).to.have.been.calledWith(
  110. 'deferred-emails',
  111. { data: { emailType: ctx.emailType, opts: ctx.opts } },
  112. ctx.ONE_HOUR_IN_MS
  113. )
  114. })
  115. })
  116. })