UserRegistrationHandlerTests.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /* eslint-disable
  2. handle-callback-err,
  3. max-len,
  4. no-return-assign,
  5. no-unused-vars,
  6. */
  7. // TODO: This file was created by bulk-decaffeinate.
  8. // Fix any style issues and re-enable lint.
  9. /*
  10. * decaffeinate suggestions:
  11. * DS102: Remove unnecessary code created because of implicit returns
  12. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  13. */
  14. const should = require('chai').should()
  15. const SandboxedModule = require('sandboxed-module')
  16. const assert = require('assert')
  17. const path = require('path')
  18. const modulePath = path.join(
  19. __dirname,
  20. '../../../../app/src/Features/User/UserRegistrationHandler'
  21. )
  22. const sinon = require('sinon')
  23. const { expect } = require('chai')
  24. const EmailHelper = require('../../../../app/src/Features/Helpers/EmailHelper')
  25. describe('UserRegistrationHandler', function() {
  26. beforeEach(function() {
  27. this.user = { _id: (this.user_id = '31j2lk21kjl') }
  28. this.User = { updateOne: sinon.stub().callsArgWith(2) }
  29. this.UserGetter = { getUserByAnyEmail: sinon.stub() }
  30. this.UserCreator = {
  31. createNewUser: sinon.stub().callsArgWith(2, null, this.user)
  32. }
  33. this.AuthenticationManager = {
  34. validateEmail: sinon.stub().returns(null),
  35. validatePassword: sinon.stub().returns(null),
  36. setUserPassword: sinon.stub().callsArgWith(2)
  37. }
  38. this.NewsLetterManager = { subscribe: sinon.stub().callsArgWith(1) }
  39. this.EmailHandler = { sendEmail: sinon.stub().callsArgWith(2) }
  40. this.OneTimeTokenHandler = { getNewToken: sinon.stub() }
  41. this.handler = SandboxedModule.require(modulePath, {
  42. globals: {
  43. console: console
  44. },
  45. requires: {
  46. '../../models/User': { User: this.User },
  47. './UserGetter': this.UserGetter,
  48. './UserCreator': this.UserCreator,
  49. '../Authentication/AuthenticationManager': this.AuthenticationManager,
  50. '../Newsletter/NewsletterManager': this.NewsLetterManager,
  51. 'logger-sharelatex': (this.logger = { log: sinon.stub() }),
  52. crypto: (this.crypto = {}),
  53. '../Email/EmailHandler': this.EmailHandler,
  54. '../Security/OneTimeTokenHandler': this.OneTimeTokenHandler,
  55. '../Analytics/AnalyticsManager': (this.AnalyticsManager = {
  56. recordEvent: sinon.stub()
  57. }),
  58. 'settings-sharelatex': (this.settings = {
  59. siteUrl: 'http://sl.example.com'
  60. }),
  61. '../Helpers/EmailHelper': EmailHelper
  62. }
  63. })
  64. return (this.passingRequest = {
  65. email: 'something@email.com',
  66. password: '123'
  67. })
  68. })
  69. describe('validate Register Request', function() {
  70. it('allows passing validation through', function() {
  71. const result = this.handler._registrationRequestIsValid(
  72. this.passingRequest
  73. )
  74. return result.should.equal(true)
  75. })
  76. describe('failing email validation', function() {
  77. beforeEach(function() {
  78. return this.AuthenticationManager.validateEmail.returns({
  79. message: 'email not set'
  80. })
  81. })
  82. it('does not allow through', function() {
  83. const result = this.handler._registrationRequestIsValid(
  84. this.passingRequest
  85. )
  86. return result.should.equal(false)
  87. })
  88. })
  89. describe('failing password validation', function() {
  90. beforeEach(function() {
  91. return this.AuthenticationManager.validatePassword.returns({
  92. message: 'password is too short'
  93. })
  94. })
  95. it('does not allow through', function() {
  96. const result = this.handler._registrationRequestIsValid(
  97. this.passingRequest
  98. )
  99. return result.should.equal(false)
  100. })
  101. })
  102. })
  103. describe('registerNewUser', function() {
  104. describe('holdingAccount', function(done) {
  105. beforeEach(function() {
  106. this.user.holdingAccount = true
  107. this.handler._registrationRequestIsValid = sinon.stub().returns(true)
  108. return this.UserGetter.getUserByAnyEmail.callsArgWith(
  109. 1,
  110. null,
  111. this.user
  112. )
  113. })
  114. it('should not create a new user if there is a holding account there', function(done) {
  115. return this.handler.registerNewUser(this.passingRequest, err => {
  116. this.UserCreator.createNewUser.called.should.equal(false)
  117. return done()
  118. })
  119. })
  120. it('should set holding account to false', function(done) {
  121. return this.handler.registerNewUser(this.passingRequest, err => {
  122. const update = this.User.updateOne.args[0]
  123. assert.deepEqual(update[0], { _id: this.user._id })
  124. assert.deepEqual(update[1], { $set: { holdingAccount: false } })
  125. return done()
  126. })
  127. })
  128. })
  129. describe('invalidRequest', function() {
  130. it('should not create a new user if the the request is not valid', function(done) {
  131. this.handler._registrationRequestIsValid = sinon.stub().returns(false)
  132. return this.handler.registerNewUser(this.passingRequest, err => {
  133. expect(err).to.exist
  134. this.UserCreator.createNewUser.called.should.equal(false)
  135. return done()
  136. })
  137. })
  138. it('should return email registered in the error if there is a non holdingAccount there', function(done) {
  139. this.UserGetter.getUserByAnyEmail.callsArgWith(
  140. 1,
  141. null,
  142. (this.user = { holdingAccount: false })
  143. )
  144. return this.handler.registerNewUser(
  145. this.passingRequest,
  146. (err, user) => {
  147. expect(err).to.be.instanceOf(Error)
  148. expect(err).to.have.property('message', 'EmailAlreadyRegistered')
  149. user.should.deep.equal(this.user)
  150. return done()
  151. }
  152. )
  153. })
  154. })
  155. describe('validRequest', function() {
  156. beforeEach(function() {
  157. this.handler._registrationRequestIsValid = sinon.stub().returns(true)
  158. return this.UserGetter.getUserByAnyEmail.callsArgWith(1)
  159. })
  160. it('should create a new user', function(done) {
  161. return this.handler.registerNewUser(this.passingRequest, err => {
  162. this.UserCreator.createNewUser
  163. .calledWith({
  164. email: this.passingRequest.email,
  165. holdingAccount: false,
  166. first_name: this.passingRequest.first_name,
  167. last_name: this.passingRequest.last_name
  168. })
  169. .should.equal(true)
  170. return done()
  171. })
  172. })
  173. it('lower case email', function(done) {
  174. this.passingRequest.email = 'soMe@eMail.cOm'
  175. return this.handler.registerNewUser(this.passingRequest, err => {
  176. this.UserCreator.createNewUser.args[0][0].email.should.equal(
  177. 'some@email.com'
  178. )
  179. return done()
  180. })
  181. })
  182. it('trim white space from email', function(done) {
  183. this.passingRequest.email = ' some@email.com '
  184. return this.handler.registerNewUser(this.passingRequest, err => {
  185. this.UserCreator.createNewUser.args[0][0].email.should.equal(
  186. 'some@email.com'
  187. )
  188. return done()
  189. })
  190. })
  191. it('should set the password', function(done) {
  192. return this.handler.registerNewUser(this.passingRequest, err => {
  193. this.AuthenticationManager.setUserPassword
  194. .calledWith(this.user._id, this.passingRequest.password)
  195. .should.equal(true)
  196. return done()
  197. })
  198. })
  199. it('should add the user to the newsletter if accepted terms', function(done) {
  200. this.passingRequest.subscribeToNewsletter = 'true'
  201. return this.handler.registerNewUser(this.passingRequest, err => {
  202. this.NewsLetterManager.subscribe
  203. .calledWith(this.user)
  204. .should.equal(true)
  205. return done()
  206. })
  207. })
  208. it('should not add the user to the newsletter if not accepted terms', function(done) {
  209. return this.handler.registerNewUser(this.passingRequest, err => {
  210. this.NewsLetterManager.subscribe
  211. .calledWith(this.user)
  212. .should.equal(false)
  213. return done()
  214. })
  215. })
  216. it('should track the registration event', function(done) {
  217. return this.handler.registerNewUser(this.passingRequest, err => {
  218. this.AnalyticsManager.recordEvent
  219. .calledWith(this.user._id, 'user-registered')
  220. .should.equal(true)
  221. return done()
  222. })
  223. })
  224. })
  225. it('should call the ReferalAllocator', function(done) {
  226. return done()
  227. })
  228. })
  229. describe('registerNewUserAndSendActivationEmail', function() {
  230. beforeEach(function() {
  231. this.email = 'email@example.com'
  232. this.crypto.randomBytes = sinon.stub().returns({
  233. toString: () => {
  234. return (this.password = 'mock-password')
  235. }
  236. })
  237. this.OneTimeTokenHandler.getNewToken.yields(
  238. null,
  239. (this.token = 'mock-token')
  240. )
  241. this.handler.registerNewUser = sinon.stub()
  242. return (this.callback = sinon.stub())
  243. })
  244. describe('with a new user', function() {
  245. beforeEach(function() {
  246. this.handler.registerNewUser.callsArgWith(1, null, this.user)
  247. return this.handler.registerNewUserAndSendActivationEmail(
  248. this.email,
  249. this.callback
  250. )
  251. })
  252. it('should ask the UserRegistrationHandler to register user', function() {
  253. return this.handler.registerNewUser
  254. .calledWith({
  255. email: this.email,
  256. password: this.password
  257. })
  258. .should.equal(true)
  259. })
  260. it('should generate a new password reset token', function() {
  261. const data = { user_id: this.user._id.toString(), email: this.email }
  262. return this.OneTimeTokenHandler.getNewToken
  263. .calledWith('password', data, { expiresIn: 7 * 24 * 60 * 60 })
  264. .should.equal(true)
  265. })
  266. it('should send a registered email', function() {
  267. return this.EmailHandler.sendEmail
  268. .calledWith('registered', {
  269. to: this.user.email,
  270. setNewPasswordUrl: `${this.settings.siteUrl}/user/activate?token=${
  271. this.token
  272. }&user_id=${this.user_id}`
  273. })
  274. .should.equal(true)
  275. })
  276. it('should return the user', function() {
  277. return this.callback
  278. .calledWith(
  279. null,
  280. this.user,
  281. `${this.settings.siteUrl}/user/activate?token=${
  282. this.token
  283. }&user_id=${this.user_id}`
  284. )
  285. .should.equal(true)
  286. })
  287. })
  288. describe('with a user that already exists', function() {
  289. beforeEach(function() {
  290. this.handler.registerNewUser.callsArgWith(
  291. 1,
  292. new Error('EmailAlreadyRegistered'),
  293. this.user
  294. )
  295. return this.handler.registerNewUserAndSendActivationEmail(
  296. this.email,
  297. this.callback
  298. )
  299. })
  300. it('should still generate a new password token and email', function() {
  301. this.OneTimeTokenHandler.getNewToken.called.should.equal(true)
  302. return this.EmailHandler.sendEmail.called.should.equal(true)
  303. })
  304. })
  305. })
  306. })