NotificationsHandler.test.mjs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import { vi, assert } from 'vitest'
  2. import sinon from 'sinon'
  3. import path from 'node:path'
  4. const modulePath = path.join(
  5. import.meta.dirname,
  6. '../../../../app/src/Features/Notifications/NotificationsHandler.mjs'
  7. )
  8. describe('NotificationsHandler', function () {
  9. const userId = '123nd3ijdks'
  10. const notificationId = '123njdskj9jlk'
  11. const notificationUrl = 'http://notification.overleaf.testing'
  12. beforeEach(async function (ctx) {
  13. ctx.FetchUtils = {
  14. fetchJson: sinon.stub().resolves(),
  15. fetchNothing: sinon.stub().resolves(),
  16. }
  17. vi.doMock('@overleaf/settings', () => ({
  18. default: {
  19. apis: { notifications: { url: notificationUrl } },
  20. },
  21. }))
  22. vi.doMock('@overleaf/fetch-utils', () => ctx.FetchUtils)
  23. ctx.handler = (await import(modulePath)).default
  24. })
  25. describe('getUserNotifications', function () {
  26. it('should get unread notifications', async function (ctx) {
  27. const stubbedNotifications = [{ _id: notificationId, user_id: userId }]
  28. ctx.FetchUtils.fetchJson.resolves(stubbedNotifications)
  29. const unreadNotifications =
  30. await ctx.handler.promises.getUserNotifications(userId)
  31. stubbedNotifications.should.deep.equal(unreadNotifications)
  32. ctx.FetchUtils.fetchJson
  33. .calledWith(
  34. sinon.match(u => u.href === `${notificationUrl}/user/${userId}`)
  35. )
  36. .should.equal(true)
  37. })
  38. it('should return empty arrays if there are no notifications', async function (ctx) {
  39. ctx.FetchUtils.fetchJson.resolves(null)
  40. const unreadNotifications =
  41. await ctx.handler.promises.getUserNotifications(userId)
  42. unreadNotifications.length.should.equal(0)
  43. })
  44. })
  45. describe('markAsRead', function () {
  46. beforeEach(function (ctx) {
  47. ctx.key = 'some key here'
  48. })
  49. it('should send a delete request when a delete has been received to mark a notification', async function (ctx) {
  50. await ctx.handler.promises.markAsReadWithKey(userId, ctx.key)
  51. ctx.FetchUtils.fetchNothing
  52. .calledWith(
  53. sinon.match(u => u.href === `${notificationUrl}/user/${userId}`),
  54. sinon.match({
  55. method: 'DELETE',
  56. json: { key: ctx.key },
  57. })
  58. )
  59. .should.equal(true)
  60. })
  61. })
  62. describe('createNotification', function () {
  63. beforeEach(function (ctx) {
  64. ctx.key = 'some key here'
  65. ctx.messageOpts = { value: 12344 }
  66. ctx.templateKey = 'renderThisHtml'
  67. ctx.expiry = null
  68. })
  69. it('should post the message over', async function (ctx) {
  70. await ctx.handler.promises.createNotification(
  71. userId,
  72. ctx.key,
  73. ctx.templateKey,
  74. ctx.messageOpts,
  75. ctx.expiry
  76. )
  77. const [url, opts] = ctx.FetchUtils.fetchNothing.getCall(0).args
  78. url.href.should.equal(`${notificationUrl}/user/${userId}`)
  79. opts.method.should.equal('POST')
  80. const expectedJson = {
  81. key: ctx.key,
  82. templateKey: ctx.templateKey,
  83. messageOpts: ctx.messageOpts,
  84. forceCreate: true,
  85. }
  86. assert.deepEqual(opts.json, expectedJson)
  87. })
  88. describe('when expiry date is supplied', function () {
  89. beforeEach(function (ctx) {
  90. ctx.key = 'some key here'
  91. ctx.messageOpts = { value: 12344 }
  92. ctx.templateKey = 'renderThisHtml'
  93. ctx.expiry = new Date()
  94. })
  95. it('should post the message over with expiry field', async function (ctx) {
  96. await ctx.handler.promises.createNotification(
  97. userId,
  98. ctx.key,
  99. ctx.templateKey,
  100. ctx.messageOpts,
  101. ctx.expiry
  102. )
  103. const [url, args] = ctx.FetchUtils.fetchNothing.getCall(0).args
  104. url.href.should.equal(`${notificationUrl}/user/${userId}`)
  105. args.method.should.equal('POST')
  106. const expectedJson = {
  107. key: ctx.key,
  108. templateKey: ctx.templateKey,
  109. messageOpts: ctx.messageOpts,
  110. expires: ctx.expiry,
  111. forceCreate: true,
  112. }
  113. assert.deepEqual(args.json, expectedJson)
  114. })
  115. })
  116. })
  117. describe('markAsReadByKeyOnly', function () {
  118. beforeEach(function (ctx) {
  119. ctx.key = 'some key here'
  120. })
  121. it('should send a delete request when a delete has been received to mark a notification', async function (ctx) {
  122. await ctx.handler.promises.markAsReadByKeyOnly(ctx.key)
  123. ctx.FetchUtils.fetchNothing
  124. .calledWith(
  125. sinon.match(
  126. u => u.href === `${notificationUrl}/key/some%20key%20here`
  127. ),
  128. sinon.match({
  129. method: 'DELETE',
  130. })
  131. )
  132. .should.equal(true)
  133. })
  134. })
  135. })