ProjectCRUDTests.mjs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import { expect } from 'chai'
  2. import UserHelper from './helpers/User.mjs'
  3. import { Project } from '../../../app/src/models/Project.js'
  4. import mongodb from 'mongodb-legacy'
  5. import cheerio from 'cheerio'
  6. import { Subscription } from '../../../app/src/models/Subscription.js'
  7. import Features from '../../../app/src/infrastructure/Features.js'
  8. const ObjectId = mongodb.ObjectId
  9. const User = UserHelper.promises
  10. describe('Project CRUD', function () {
  11. beforeEach(async function () {
  12. this.user = new User()
  13. await this.user.login()
  14. this.projectId = await this.user.createProject('example-project')
  15. })
  16. describe('project page', function () {
  17. const loadProject = async function (user, projectId) {
  18. const { response, body } = await user.doRequest(
  19. 'GET',
  20. `/project/${projectId}`
  21. )
  22. expect(response.statusCode).to.equal(200)
  23. return body
  24. }
  25. it('should cast refProviders to booleans', async function () {
  26. await this.user.mongoUpdate({
  27. $set: {
  28. refProviders: {
  29. mendeley: { encrypted: 'aaa' },
  30. zotero: { encrypted: 'bbb' },
  31. },
  32. },
  33. })
  34. const body = await loadProject(this.user, this.projectId)
  35. const dom = cheerio.load(body)
  36. const metaOlUser = dom('meta[name="ol-user"]')[0]
  37. const userData = JSON.parse(metaOlUser.attribs.content)
  38. expect(userData.refProviders.mendeley).to.equal(true)
  39. expect(userData.refProviders.zotero).to.equal(true)
  40. })
  41. it('should show UpgradePrompt for user without a subscription', async function () {
  42. const body = await loadProject(this.user, this.projectId)
  43. expect(body).to.include(
  44. Features.hasFeature('saas')
  45. ? // `content` means true in this context
  46. '<meta name="ol-showUpgradePrompt" data-type="boolean" content>'
  47. : '<meta name="ol-showUpgradePrompt" data-type="boolean">'
  48. )
  49. })
  50. it('should not show UpgradePrompt for user with a subscription', async function () {
  51. await Subscription.create({
  52. admin_id: this.user._id,
  53. manager_ids: [this.user._id],
  54. })
  55. const body = await loadProject(this.user, this.projectId)
  56. // having no `content` means false in this context
  57. expect(body).to.include(
  58. '<meta name="ol-showUpgradePrompt" data-type="boolean">'
  59. )
  60. })
  61. })
  62. describe("when project doesn't exist", function () {
  63. it('should return 404', async function () {
  64. const { response } = await this.user.doRequest(
  65. 'GET',
  66. '/project/aaaaaaaaaaaaaaaaaaaaaaaa'
  67. )
  68. expect(response.statusCode).to.equal(404)
  69. })
  70. })
  71. describe('when project has malformed id', function () {
  72. it('should return 404', async function () {
  73. const { response } = await this.user.doRequest('GET', '/project/blah')
  74. expect(response.statusCode).to.equal(404)
  75. })
  76. })
  77. describe('when trashing a project', function () {
  78. it('should mark the project as trashed for the user', async function () {
  79. const { response } = await this.user.doRequest(
  80. 'POST',
  81. `/project/${this.projectId}/trash`
  82. )
  83. expect(response.statusCode).to.equal(200)
  84. const trashedProject = await Project.findById(this.projectId).exec()
  85. expectObjectIdArrayEqual(trashedProject.trashed, [this.user._id])
  86. })
  87. it('does nothing if the user has already trashed the project', async function () {
  88. // Mark as trashed the first time
  89. await this.user.doRequest('POST', `/project/${this.projectId}/trash`)
  90. // And then a second time
  91. await this.user.doRequest('POST', `/project/${this.projectId}/trash`)
  92. const trashedProject = await Project.findById(this.projectId).exec()
  93. expectObjectIdArrayEqual(trashedProject.trashed, [this.user._id])
  94. })
  95. describe('with an array archived state', function () {
  96. it('should mark the project as not archived for the user', async function () {
  97. await Project.updateOne(
  98. { _id: this.projectId },
  99. { $set: { archived: [new ObjectId(this.user._id)] } }
  100. ).exec()
  101. const { response } = await this.user.doRequest(
  102. 'POST',
  103. `/project/${this.projectId}/trash`
  104. )
  105. expect(response.statusCode).to.equal(200)
  106. const trashedProject = await Project.findById(this.projectId).exec()
  107. expectObjectIdArrayEqual(trashedProject.archived, [])
  108. })
  109. })
  110. })
  111. describe('when untrashing a project', function () {
  112. it('should mark the project as untrashed for the user', async function () {
  113. await Project.updateOne(
  114. { _id: this.projectId },
  115. { trashed: [new ObjectId(this.user._id)] }
  116. ).exec()
  117. const { response } = await this.user.doRequest(
  118. 'DELETE',
  119. `/project/${this.projectId}/trash`
  120. )
  121. expect(response.statusCode).to.equal(200)
  122. const trashedProject = await Project.findById(this.projectId).exec()
  123. expectObjectIdArrayEqual(trashedProject.trashed, [])
  124. })
  125. it('does nothing if the user has already untrashed the project', async function () {
  126. await Project.updateOne(
  127. { _id: this.projectId },
  128. { trashed: [new ObjectId(this.user._id)] }
  129. ).exec()
  130. // Mark as untrashed the first time
  131. await this.user.doRequest('DELETE', `/project/${this.projectId}/trash`)
  132. // And then a second time
  133. await this.user.doRequest('DELETE', `/project/${this.projectId}/trash`)
  134. const trashedProject = await Project.findById(this.projectId).exec()
  135. expectObjectIdArrayEqual(trashedProject.trashed, [])
  136. })
  137. it('sets trashed to an empty array if not set', async function () {
  138. await this.user.doRequest('DELETE', `/project/${this.projectId}/trash`)
  139. const trashedProject = await Project.findById(this.projectId).exec()
  140. expectObjectIdArrayEqual(trashedProject.trashed, [])
  141. })
  142. })
  143. describe('ProjectAdminSettings', async function () {
  144. it('publicAccessLevel can be set to private', async function () {
  145. const { response } = await this.user.doRequest('POST', {
  146. url: `/project/${this.projectId}/settings/admin`,
  147. json: {
  148. publicAccessLevel: 'private',
  149. },
  150. })
  151. expect(response.statusCode).to.equal(204)
  152. const project = await Project.findById(this.projectId).exec()
  153. expect(project.publicAccesLevel).to.equal('private')
  154. })
  155. it('publicAccessLevel can be set to tokenBased', async function () {
  156. await this.user.makePrivate(this.projectId)
  157. const { response } = await this.user.doRequest('POST', {
  158. url: `/project/${this.projectId}/settings/admin`,
  159. json: {
  160. publicAccessLevel: 'tokenBased',
  161. },
  162. })
  163. expect(response.statusCode).to.equal(204)
  164. const project = await Project.findById(this.projectId).exec()
  165. expect(project.publicAccesLevel).to.equal('tokenBased')
  166. })
  167. it('returns a 400 when publicAccessLevel is an unsupported access level', async function () {
  168. await this.user.makePrivate(this.projectId)
  169. const { response, body } = await this.user.doRequest('POST', {
  170. url: `/project/${this.projectId}/settings/admin`,
  171. json: {
  172. publicAccessLevel: 'readOnly',
  173. },
  174. })
  175. expect(response.statusCode).to.equal(400)
  176. expect(body.details[0].message).to.equal('unexpected access level')
  177. const project = await Project.findById(this.projectId).exec()
  178. expect(project.publicAccesLevel).to.equal('private')
  179. })
  180. it('returns a 500 when no publicAccessLevel is provided', async function () {
  181. const { response, body } = await this.user.doRequest('POST', {
  182. url: `/project/${this.projectId}/settings/admin`,
  183. json: {},
  184. })
  185. expect(response.statusCode).to.equal(500)
  186. expect(body).to.equal('Internal Server Error')
  187. })
  188. })
  189. })
  190. function expectObjectIdArrayEqual(objectIdArray, stringArray) {
  191. const stringifiedArray = objectIdArray.map(id => id.toString())
  192. expect(stringifiedArray).to.deep.equal(stringArray)
  193. }