BetaProgramController.test.mjs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import { expect, vi } from 'vitest'
  2. import path from 'node:path'
  3. import sinon from 'sinon'
  4. import MockResponse from '../helpers/MockResponse.mjs'
  5. import { fileURLToPath } from 'node:url'
  6. const __dirname = fileURLToPath(new URL('.', import.meta.url))
  7. const modulePath = path.join(
  8. __dirname,
  9. '../../../../app/src/Features/BetaProgram/BetaProgramController'
  10. )
  11. describe('BetaProgramController', function () {
  12. beforeEach(async function (ctx) {
  13. ctx.user = {
  14. _id: (ctx.user_id = 'a_simple_id'),
  15. email: 'user@example.com',
  16. features: {},
  17. betaProgram: false,
  18. }
  19. ctx.req = {
  20. query: {},
  21. session: {
  22. user: ctx.user,
  23. },
  24. }
  25. ctx.SplitTestSessionHandler = {
  26. promises: {
  27. sessionMaintenance: sinon.stub(),
  28. },
  29. }
  30. vi.doMock(
  31. '../../../../app/src/Features/SplitTests/SplitTestSessionHandler',
  32. () => ({
  33. default: ctx.SplitTestSessionHandler,
  34. })
  35. )
  36. vi.doMock(
  37. '../../../../app/src/Features/BetaProgram/BetaProgramHandler',
  38. () => ({
  39. default: (ctx.BetaProgramHandler = {
  40. promises: {
  41. optIn: sinon.stub().resolves(),
  42. optOut: sinon.stub().resolves(),
  43. },
  44. }),
  45. })
  46. )
  47. vi.doMock('../../../../app/src/Features/User/UserGetter', () => ({
  48. default: (ctx.UserGetter = {
  49. promises: {
  50. getUser: sinon.stub().resolves(),
  51. },
  52. }),
  53. }))
  54. vi.doMock('@overleaf/settings', () => ({
  55. default: (ctx.settings = {
  56. languages: {},
  57. }),
  58. }))
  59. vi.doMock(
  60. '../../../../app/src/Features/Authentication/AuthenticationController',
  61. () => ({
  62. default: (ctx.AuthenticationController = {
  63. getLoggedInUserId: sinon.stub().returns(ctx.user._id),
  64. }),
  65. })
  66. )
  67. ctx.BetaProgramController = (await import(modulePath)).default
  68. ctx.res = new MockResponse(vi)
  69. ctx.next = sinon.stub()
  70. })
  71. describe('optIn', function () {
  72. it("should redirect to '/beta/participate'", async function (ctx) {
  73. await new Promise((resolve, reject) => {
  74. ctx.res.callback = () => {
  75. ctx.res.redirectedTo.should.equal('/beta/participate')
  76. resolve()
  77. }
  78. ctx.BetaProgramController.optIn(
  79. ctx.req,
  80. ctx.res,
  81. ctx.rejectOnError(reject)
  82. )
  83. })
  84. })
  85. it('should not call next with an error', function (ctx) {
  86. ctx.BetaProgramController.optIn(ctx.req, ctx.res, ctx.next)
  87. ctx.next.callCount.should.equal(0)
  88. })
  89. it('should call BetaProgramHandler.optIn', function (ctx) {
  90. ctx.BetaProgramController.optIn(ctx.req, ctx.res, ctx.next)
  91. ctx.BetaProgramHandler.promises.optIn.callCount.should.equal(1)
  92. })
  93. it('should invoke the session maintenance', async function (ctx) {
  94. await new Promise(resolve => {
  95. ctx.res.callback = () => {
  96. ctx.SplitTestSessionHandler.promises.sessionMaintenance.should.have.been.calledWith(
  97. ctx.req
  98. )
  99. resolve()
  100. }
  101. ctx.BetaProgramController.optIn(ctx.req, ctx.res)
  102. })
  103. })
  104. describe('when BetaProgramHandler.opIn produces an error', function () {
  105. beforeEach(function (ctx) {
  106. ctx.BetaProgramHandler.promises.optIn.throws(new Error('woops'))
  107. })
  108. it("should not redirect to '/beta/participate'", function (ctx) {
  109. ctx.BetaProgramController.optIn(ctx.req, ctx.res, ctx.next)
  110. expect(ctx.res.redirect).not.toHaveBeenCalled()
  111. })
  112. it('should produce an error', async function (ctx) {
  113. await new Promise(resolve => {
  114. ctx.BetaProgramController.optIn(ctx.req, ctx.res, err => {
  115. expect(err).to.be.instanceof(Error)
  116. resolve()
  117. })
  118. })
  119. })
  120. })
  121. })
  122. describe('optOut', function () {
  123. it("should redirect to '/beta/participate'", async function (ctx) {
  124. await new Promise((resolve, reject) => {
  125. ctx.res.callback = () => {
  126. expect(ctx.res.redirectedTo).to.equal('/beta/participate')
  127. resolve()
  128. }
  129. ctx.BetaProgramController.optOut(
  130. ctx.req,
  131. ctx.res,
  132. ctx.rejectOnError(reject)
  133. )
  134. })
  135. })
  136. it('should not call next with an error', async function (ctx) {
  137. await new Promise((resolve, reject) => {
  138. ctx.res.callback = () => {
  139. ctx.next.callCount.should.equal(0)
  140. resolve()
  141. }
  142. ctx.BetaProgramController.optOut(
  143. ctx.req,
  144. ctx.res,
  145. ctx.rejectOnError(reject)
  146. )
  147. })
  148. })
  149. it('should call BetaProgramHandler.optOut', async function (ctx) {
  150. await new Promise((resolve, reject) => {
  151. ctx.res.callback = () => {
  152. ctx.BetaProgramHandler.promises.optOut.callCount.should.equal(1)
  153. resolve()
  154. }
  155. ctx.BetaProgramController.optOut(
  156. ctx.req,
  157. ctx.res,
  158. ctx.rejectOnError(reject)
  159. )
  160. })
  161. })
  162. it('should invoke the session maintenance', async function (ctx) {
  163. await new Promise((resolve, reject) => {
  164. ctx.res.callback = () => {
  165. ctx.SplitTestSessionHandler.promises.sessionMaintenance.should.have.been.calledWith(
  166. ctx.req,
  167. null
  168. )
  169. resolve()
  170. }
  171. ctx.BetaProgramController.optOut(
  172. ctx.req,
  173. ctx.res,
  174. ctx.rejectOnError(reject)
  175. )
  176. })
  177. })
  178. describe('when BetaProgramHandler.optOut produces an error', function () {
  179. beforeEach(function (ctx) {
  180. ctx.BetaProgramHandler.promises.optOut.throws(new Error('woops'))
  181. })
  182. it("should not redirect to '/beta/participate'", async function (ctx) {
  183. await new Promise(resolve => {
  184. ctx.BetaProgramController.optOut(ctx.req, ctx.res, error => {
  185. expect(error).to.exist
  186. expect(ctx.res.redirected).to.equal(false)
  187. resolve()
  188. })
  189. })
  190. })
  191. it('should produce an error', async function (ctx) {
  192. await new Promise(resolve => {
  193. ctx.BetaProgramController.optOut(ctx.req, ctx.res, error => {
  194. expect(error).to.exist
  195. resolve()
  196. })
  197. })
  198. })
  199. })
  200. })
  201. describe('optInPage', function () {
  202. beforeEach(function (ctx) {
  203. ctx.UserGetter.promises.getUser.resolves(ctx.user)
  204. })
  205. it('should render the opt-in page', async function (ctx) {
  206. await new Promise((resolve, reject) => {
  207. ctx.res.callback = () => {
  208. expect(ctx.res.renderedTemplate).to.equal('beta_program/opt_in')
  209. resolve()
  210. }
  211. ctx.BetaProgramController.optInPage(
  212. ctx.req,
  213. ctx.res,
  214. ctx.rejectOnError(reject)
  215. )
  216. })
  217. })
  218. describe('when UserGetter.getUser produces an error', function () {
  219. beforeEach(function (ctx) {
  220. ctx.UserGetter.promises.getUser.throws(new Error('woops'))
  221. })
  222. it('should not render the opt-in page', function (ctx) {
  223. ctx.BetaProgramController.optInPage(ctx.req, ctx.res, ctx.next)
  224. expect(ctx.res.render).not.toHaveBeenCalled()
  225. })
  226. it('should produce an error', async function (ctx) {
  227. await new Promise(resolve => {
  228. ctx.BetaProgramController.optInPage(ctx.req, ctx.res, error => {
  229. expect(error).to.exist
  230. expect(error).to.be.instanceof(Error)
  231. resolve()
  232. })
  233. })
  234. })
  235. })
  236. })
  237. })