ProjectInviteTests.coffee 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. expect = require("chai").expect
  2. Async = require("async")
  3. User = require "./helpers/User"
  4. request = require "./helpers/request"
  5. settings = require "settings-sharelatex"
  6. CollaboratorsEmailHandler = require "../../../app/js/Features/Collaborators/CollaboratorsEmailHandler"
  7. createInvite = (sendingUser, projectId, email, callback=(err, invite)->) ->
  8. sendingUser.getCsrfToken (err) ->
  9. return callback(err) if err
  10. sendingUser.request.post {
  11. url: "/project/#{projectId}/invite",
  12. json:
  13. email: email
  14. privileges: 'readAndWrite'
  15. }, (err, response, body) ->
  16. return callback(err) if err
  17. callback(null, body.invite)
  18. createProject = (owner, projectName, callback=(err, projectId, project)->) ->
  19. owner.createProject projectName, (err, projectId) ->
  20. throw err if err
  21. fakeProject = {
  22. _id: projectId,
  23. name: projectName,
  24. owner_ref: owner
  25. }
  26. callback(err, projectId, fakeProject)
  27. createProjectAndInvite = (owner, projectName, email, callback=(err, project, invite)->) ->
  28. createProject owner, projectName, (err, projectId, project) ->
  29. return callback(err) if err
  30. createInvite owner, projectId, email, (err, invite) ->
  31. return callback(err) if err
  32. link = CollaboratorsEmailHandler._buildInviteUrl(project, invite)
  33. callback(null, project, invite, link)
  34. revokeInvite = (sendingUser, projectId, inviteId, callback=(err)->) ->
  35. sendingUser.getCsrfToken (err) ->
  36. return callback(err) if err
  37. sendingUser.request.delete {
  38. url: "/project/#{projectId}/invite/#{inviteId}",
  39. }, (err, response, body) ->
  40. return callback(err) if err
  41. callback(null)
  42. # Actions
  43. tryFollowInviteLink = (user, link, callback=(err, response, body)->) ->
  44. user.request.get {
  45. uri: link
  46. baseUrl: null
  47. }, callback
  48. tryAcceptInvite = (user, invite, callback=(err, response, body)->) ->
  49. user.request.post {
  50. uri: "/project/#{invite.projectId}/invite/#{invite._id}/accept"
  51. json:
  52. token: invite.token
  53. }, callback
  54. # Expectations
  55. expectProjectAccess = (user, projectId, callback=()->) ->
  56. # should have access to project
  57. user.openProject projectId, (err) =>
  58. expect(err).to.be.oneOf [null, undefined]
  59. callback()
  60. expectNoProjectAccess = (user, projectId, callback=()->) ->
  61. # should not have access to project page
  62. user.openProject projectId, (err) =>
  63. expect(err).to.be.instanceof Error
  64. callback()
  65. expectInvitePage = (user, link, callback=()->) ->
  66. # view invite
  67. tryFollowInviteLink user, link, (err, response, body) ->
  68. expect(err).to.be.oneOf [null, undefined]
  69. expect(response.statusCode).to.equal 200
  70. expect(body).to.match new RegExp("<title>Project Invite - .*</title>")
  71. callback()
  72. expectInvalidInvitePage = (user, link, callback=()->) ->
  73. # view invalid invite
  74. tryFollowInviteLink user, link, (err, response, body) ->
  75. expect(err).to.be.oneOf [null, undefined]
  76. expect(response.statusCode).to.equal 200
  77. expect(body).to.match new RegExp("<title>Invalid Invite - .*</title>")
  78. callback()
  79. expectInviteRedirectToRegister = (user, link, callback=()->) ->
  80. # view invite, redirect to `/register`
  81. tryFollowInviteLink user, link, (err, response, body) ->
  82. expect(err).to.be.oneOf [null, undefined]
  83. expect(response.statusCode).to.equal 302
  84. expect(response.headers.location).to.match new RegExp("^/register\?.*redir=.*$")
  85. callback()
  86. expectInviteRedirectToProject = (user, link, invite, callback=()->) ->
  87. # view invite, redirect straight to project
  88. tryFollowInviteLink user, link, (err, response, body) ->
  89. expect(err).to.be.oneOf [null, undefined]
  90. expect(response.statusCode).to.equal 302
  91. expect(response.headers.location).to.equal "/project/#{invite.projectId}"
  92. callback()
  93. expectAcceptInviteAndRedirect = (user, invite, callback=()->) ->
  94. # should accept the invite and redirect to project
  95. tryAcceptInvite user, invite, (err, response, body) =>
  96. expect(err).to.be.oneOf [null, undefined]
  97. expect(response.statusCode).to.equal 302
  98. expect(response.headers.location).to.equal "/project/#{invite.projectId}"
  99. callback()
  100. describe "ProjectInviteTests", ->
  101. before (done) ->
  102. @sendingUser = new User()
  103. @user = new User()
  104. @site_admin = new User({email: "admin@example.com"})
  105. @email = 'smoketestuser@example.com'
  106. @projectName = 'sharing test'
  107. Async.series [
  108. (cb) => @user.login cb
  109. (cb) => @user.logout cb
  110. (cb) => @sendingUser.login cb
  111. ], done
  112. describe 'clicking the invite link', ->
  113. beforeEach (done) ->
  114. @projectId = null
  115. @fakeProject = null
  116. done()
  117. describe "user is logged in already", ->
  118. beforeEach (done) ->
  119. Async.series [
  120. (cb) =>
  121. createProjectAndInvite @sendingUser, @projectName, @email, (err, project, invite, link) =>
  122. @projectId = project._id
  123. @fakeProject = project
  124. @invite = invite
  125. @link = link
  126. cb()
  127. (cb) =>
  128. @user.login (err) =>
  129. if err
  130. throw err
  131. cb()
  132. ], done
  133. afterEach (done) ->
  134. Async.series [
  135. (cb) => @sendingUser.deleteProject(@projectId, cb)
  136. (cb) => @sendingUser.deleteProject(@projectId, cb)
  137. (cb) => revokeInvite(@sendingUser, @projectId, @invite._id, cb)
  138. ], done
  139. describe 'user is already a member of the project', ->
  140. beforeEach (done) ->
  141. Async.series [
  142. (cb) =>
  143. expectInvitePage @user, @link, cb
  144. (cb) =>
  145. expectAcceptInviteAndRedirect @user, @invite, cb
  146. ], done
  147. describe 'when user clicks on the invite a second time', ->
  148. it 'should just redirect to the project page', (done) ->
  149. Async.series [
  150. (cb) =>
  151. expectProjectAccess @user, @invite.projectId, cb
  152. (cb) =>
  153. expectInviteRedirectToProject @user, @link, @invite, cb
  154. ], done
  155. describe 'user is not a member of the project', ->
  156. it 'should not grant access if the user does not accept the invite', (done) ->
  157. Async.series(
  158. [
  159. (cb) =>
  160. expectInvitePage @user, @link, cb
  161. (cb) =>
  162. expectNoProjectAccess @user, @invite.projectId, cb
  163. ], done
  164. )
  165. it 'should render the invalid-invite page if the token is invalid', (done) ->
  166. Async.series(
  167. [
  168. (cb) =>
  169. link = @link.replace(@invite.token, 'not_a_real_token')
  170. expectInvalidInvitePage @user, link, cb
  171. (cb) =>
  172. expectNoProjectAccess @user, @invite.projectId, cb
  173. ], done
  174. )
  175. it 'should allow the user to accept the invite and access the project', (done) ->
  176. Async.series(
  177. [
  178. (cb) =>
  179. expectInvitePage @user, @link, cb
  180. (cb) =>
  181. expectAcceptInviteAndRedirect @user, @invite, cb
  182. (cb) =>
  183. expectProjectAccess @user, @invite.projectId, cb
  184. ], done
  185. )
  186. describe 'user is not logged in initially', ->
  187. before (done) ->
  188. @user.logout done
  189. beforeEach (done) ->
  190. Async.series [
  191. (cb) =>
  192. createProjectAndInvite @sendingUser, @projectName, @email, (err, project, invite, link) =>
  193. @projectId = project._id
  194. @fakeProject = project
  195. @invite = invite
  196. @link = link
  197. cb()
  198. ], done
  199. afterEach (done) ->
  200. Async.series [
  201. (cb) => @sendingUser.deleteProject(@projectId, cb)
  202. (cb) => @sendingUser.deleteProject(@projectId, cb)
  203. (cb) => revokeInvite(@sendingUser, @projectId, @invite._id, cb)
  204. ], done
  205. describe 'registration prompt workflow', ->
  206. it 'should redirect to the register page', (done) ->
  207. Async.series [
  208. (cb) => expectInviteRedirectToRegister(@user, @link, cb)
  209. ], done