ProjectEditorHandlerTests.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. /* eslint-disable
  2. max-len,
  3. no-return-assign,
  4. */
  5. // TODO: This file was created by bulk-decaffeinate.
  6. // Fix any style issues and re-enable lint.
  7. /*
  8. * decaffeinate suggestions:
  9. * DS101: Remove unnecessary use of Array.from
  10. * DS102: Remove unnecessary code created because of implicit returns
  11. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  12. */
  13. const { expect } = require('chai')
  14. const modulePath = '../../../../app/src/Features/Project/ProjectEditorHandler'
  15. const SandboxedModule = require('sandboxed-module')
  16. describe('ProjectEditorHandler', function () {
  17. beforeEach(function () {
  18. this.project = {
  19. _id: 'project-id',
  20. name: 'Project Name',
  21. rootDoc_id: 'file-id',
  22. publicAccesLevel: 'private',
  23. deletedByExternalDataSource: false,
  24. rootFolder: [
  25. {
  26. _id: 'root-folder-id',
  27. name: '',
  28. docs: [],
  29. fileRefs: [],
  30. folders: [
  31. {
  32. _id: 'sub-folder-id',
  33. name: 'folder',
  34. docs: [
  35. {
  36. _id: 'doc-id',
  37. name: 'main.tex',
  38. lines: (this.lines = ['line 1', 'line 2', 'line 3']),
  39. },
  40. ],
  41. fileRefs: [
  42. {
  43. _id: 'file-id',
  44. name: 'image.png',
  45. created: (this.created = new Date()),
  46. size: 1234,
  47. },
  48. ],
  49. folders: [],
  50. },
  51. ],
  52. },
  53. ],
  54. deletedDocs: [
  55. {
  56. _id: 'deleted-doc-id',
  57. name: 'main.tex',
  58. deletedAt: (this.deletedAt = new Date('2017-01-01')),
  59. },
  60. ],
  61. }
  62. this.members = [
  63. {
  64. user: (this.owner = {
  65. _id: 'owner-id',
  66. first_name: 'Owner',
  67. last_name: 'ShareLaTeX',
  68. email: 'owner@sharelatex.com',
  69. }),
  70. privilegeLevel: 'owner',
  71. },
  72. {
  73. user: {
  74. _id: 'read-only-id',
  75. first_name: 'Read',
  76. last_name: 'Only',
  77. email: 'read-only@sharelatex.com',
  78. },
  79. privilegeLevel: 'readOnly',
  80. },
  81. {
  82. user: {
  83. _id: 'read-write-id',
  84. first_name: 'Read',
  85. last_name: 'Write',
  86. email: 'read-write@sharelatex.com',
  87. },
  88. privilegeLevel: 'readAndWrite',
  89. },
  90. ]
  91. this.invites = [
  92. {
  93. _id: 'invite_one',
  94. email: 'user-one@example.com',
  95. privileges: 'readOnly',
  96. projectId: this.project._id,
  97. token: 'my-secret-token1',
  98. },
  99. {
  100. _id: 'invite_two',
  101. email: 'user-two@example.com',
  102. privileges: 'readOnly',
  103. projectId: this.project._id,
  104. token: 'my-secret-token2',
  105. },
  106. ]
  107. this.deletedDocsFromDocstore = [
  108. { _id: 'deleted-doc-id-from-docstore', name: 'docstore.tex' },
  109. ]
  110. return (this.handler = SandboxedModule.require(modulePath))
  111. })
  112. describe('buildProjectModelView', function () {
  113. describe('with owner, members and invites included', function () {
  114. beforeEach(function () {
  115. return (this.result = this.handler.buildProjectModelView(
  116. this.project,
  117. this.members,
  118. this.invites,
  119. this.deletedDocsFromDocstore
  120. ))
  121. })
  122. it('should include the id', function () {
  123. expect(this.result._id).to.exist
  124. return this.result._id.should.equal('project-id')
  125. })
  126. it('should include the name', function () {
  127. expect(this.result.name).to.exist
  128. return this.result.name.should.equal('Project Name')
  129. })
  130. it('should include the root doc id', function () {
  131. expect(this.result.rootDoc_id).to.exist
  132. return this.result.rootDoc_id.should.equal('file-id')
  133. })
  134. it('should include the public access level', function () {
  135. expect(this.result.publicAccesLevel).to.exist
  136. return this.result.publicAccesLevel.should.equal('private')
  137. })
  138. it('should include the owner', function () {
  139. expect(this.result.owner).to.exist
  140. this.result.owner._id.should.equal('owner-id')
  141. this.result.owner.email.should.equal('owner@sharelatex.com')
  142. this.result.owner.first_name.should.equal('Owner')
  143. this.result.owner.last_name.should.equal('ShareLaTeX')
  144. return this.result.owner.privileges.should.equal('owner')
  145. })
  146. it('should include the deletedDocs', function () {
  147. expect(this.result.deletedDocs).to.exist
  148. this.result.deletedDocs.should.deep.equal([
  149. {
  150. // omit deletedAt field
  151. _id: this.project.deletedDocs[0]._id,
  152. name: this.project.deletedDocs[0].name,
  153. },
  154. this.deletedDocsFromDocstore[0],
  155. ])
  156. })
  157. it('invites should not include the token', function () {
  158. expect(this.result.invites[0].token).not.to.exist
  159. expect(this.result.invites[1].token).not.to.exist
  160. })
  161. it('should gather readOnly_refs and collaberators_refs into a list of members', function () {
  162. const findMember = id => {
  163. for (const member of Array.from(this.result.members)) {
  164. if (member._id === id) {
  165. return member
  166. }
  167. }
  168. return null
  169. }
  170. this.result.members.length.should.equal(2)
  171. expect(findMember('read-only-id')).to.exist
  172. findMember('read-only-id').privileges.should.equal('readOnly')
  173. findMember('read-only-id').first_name.should.equal('Read')
  174. findMember('read-only-id').last_name.should.equal('Only')
  175. findMember('read-only-id').email.should.equal(
  176. 'read-only@sharelatex.com'
  177. )
  178. expect(findMember('read-write-id')).to.exist
  179. findMember('read-write-id').privileges.should.equal('readAndWrite')
  180. findMember('read-write-id').first_name.should.equal('Read')
  181. findMember('read-write-id').last_name.should.equal('Write')
  182. return findMember('read-write-id').email.should.equal(
  183. 'read-write@sharelatex.com'
  184. )
  185. })
  186. it('should include folders in the project', function () {
  187. this.result.rootFolder[0]._id.should.equal('root-folder-id')
  188. this.result.rootFolder[0].name.should.equal('')
  189. this.result.rootFolder[0].folders[0]._id.should.equal('sub-folder-id')
  190. return this.result.rootFolder[0].folders[0].name.should.equal('folder')
  191. })
  192. it('should not duplicate folder contents', function () {
  193. this.result.rootFolder[0].docs.length.should.equal(0)
  194. return this.result.rootFolder[0].fileRefs.length.should.equal(0)
  195. })
  196. it('should include files in the project', function () {
  197. this.result.rootFolder[0].folders[0].fileRefs[0]._id.should.equal(
  198. 'file-id'
  199. )
  200. this.result.rootFolder[0].folders[0].fileRefs[0].name.should.equal(
  201. 'image.png'
  202. )
  203. this.result.rootFolder[0].folders[0].fileRefs[0].created.should.equal(
  204. this.created
  205. )
  206. return expect(this.result.rootFolder[0].folders[0].fileRefs[0].size).not
  207. .to.exist
  208. })
  209. it('should include docs in the project but not the lines', function () {
  210. this.result.rootFolder[0].folders[0].docs[0]._id.should.equal('doc-id')
  211. this.result.rootFolder[0].folders[0].docs[0].name.should.equal(
  212. 'main.tex'
  213. )
  214. return expect(this.result.rootFolder[0].folders[0].docs[0].lines).not.to
  215. .exist
  216. })
  217. it('should include invites', function () {
  218. expect(this.result.invites).to.exist
  219. return this.result.invites.should.deep.equal(this.invites)
  220. })
  221. })
  222. describe('when docstore sends a deleted doc that is also present in the project', function () {
  223. beforeEach(function () {
  224. this.deletedDocsFromDocstore.push(this.project.deletedDocs[0])
  225. this.result = this.handler.buildProjectModelView(
  226. this.project,
  227. this.members,
  228. this.invites,
  229. this.deletedDocsFromDocstore
  230. )
  231. })
  232. it('should not send any duplicate', function () {
  233. expect(this.result.deletedDocs).to.exist
  234. this.result.deletedDocs.should.deep.equal([
  235. this.project.deletedDocs[0],
  236. this.deletedDocsFromDocstore[0],
  237. ])
  238. })
  239. })
  240. describe('deletedByExternalDataSource', function () {
  241. it('should set the deletedByExternalDataSource flag to false when it is not there', function () {
  242. delete this.project.deletedByExternalDataSource
  243. const result = this.handler.buildProjectModelView(
  244. this.project,
  245. this.members,
  246. [],
  247. []
  248. )
  249. return result.deletedByExternalDataSource.should.equal(false)
  250. })
  251. it('should set the deletedByExternalDataSource flag to false when it is false', function () {
  252. const result = this.handler.buildProjectModelView(
  253. this.project,
  254. this.members,
  255. [],
  256. []
  257. )
  258. return result.deletedByExternalDataSource.should.equal(false)
  259. })
  260. it('should set the deletedByExternalDataSource flag to true when it is true', function () {
  261. this.project.deletedByExternalDataSource = true
  262. const result = this.handler.buildProjectModelView(
  263. this.project,
  264. this.members,
  265. [],
  266. []
  267. )
  268. return result.deletedByExternalDataSource.should.equal(true)
  269. })
  270. })
  271. describe('features', function () {
  272. beforeEach(function () {
  273. this.owner.features = {
  274. versioning: true,
  275. collaborators: 3,
  276. compileGroup: 'priority',
  277. compileTimeout: 96,
  278. }
  279. return (this.result = this.handler.buildProjectModelView(
  280. this.project,
  281. this.members,
  282. [],
  283. []
  284. ))
  285. })
  286. it('should copy the owner features to the project', function () {
  287. this.result.features.versioning.should.equal(
  288. this.owner.features.versioning
  289. )
  290. this.result.features.collaborators.should.equal(
  291. this.owner.features.collaborators
  292. )
  293. this.result.features.compileGroup.should.equal(
  294. this.owner.features.compileGroup
  295. )
  296. return this.result.features.compileTimeout.should.equal(
  297. this.owner.features.compileTimeout
  298. )
  299. })
  300. })
  301. describe('trackChangesState', function () {
  302. describe('when the owner does not have the trackChanges feature', function () {
  303. beforeEach(function () {
  304. this.owner.features = {
  305. trackChanges: false,
  306. }
  307. this.result = this.handler.buildProjectModelView(
  308. this.project,
  309. this.members,
  310. [],
  311. []
  312. )
  313. })
  314. it('should not emit trackChangesState', function () {
  315. expect(this.result.trackChangesState).to.not.exist
  316. })
  317. })
  318. describe('when the owner has got the trackChanges feature', function () {
  319. beforeEach(function () {
  320. this.owner.features = {
  321. trackChanges: true,
  322. }
  323. })
  324. function genCase([dbEntry, expected]) {
  325. describe(`when track_changes is ${JSON.stringify(
  326. dbEntry
  327. )}`, function () {
  328. beforeEach(function () {
  329. this.project.track_changes = dbEntry
  330. this.result = this.handler.buildProjectModelView(
  331. this.project,
  332. this.members,
  333. [],
  334. []
  335. )
  336. })
  337. it(`should set trackChangesState=${expected}`, function () {
  338. expect(this.result.trackChangesState).to.deep.equal(expected)
  339. })
  340. })
  341. }
  342. const CASES = [
  343. [null, false],
  344. [false, false],
  345. [true, true],
  346. [{ someId: true }, { someId: true }],
  347. ]
  348. CASES.map(genCase)
  349. })
  350. })
  351. })
  352. describe('buildOwnerAndMembersViews', function () {
  353. beforeEach(function () {
  354. this.owner.features = {
  355. versioning: true,
  356. collaborators: 3,
  357. compileGroup: 'priority',
  358. compileTimeout: 22,
  359. }
  360. return (this.result = this.handler.buildOwnerAndMembersViews(
  361. this.members
  362. ))
  363. })
  364. it('should produce an object with the right keys', function () {
  365. return expect(this.result).to.have.all.keys([
  366. 'owner',
  367. 'ownerFeatures',
  368. 'members',
  369. ])
  370. })
  371. it('should separate the owner from the members', function () {
  372. this.result.members.length.should.equal(this.members.length - 1)
  373. expect(this.result.owner._id).to.equal(this.owner._id)
  374. expect(this.result.owner.email).to.equal(this.owner.email)
  375. return expect(
  376. this.result.members.filter(m => m._id === this.owner._id).length
  377. ).to.equal(0)
  378. })
  379. it('should extract the ownerFeatures from the owner object', function () {
  380. return expect(this.result.ownerFeatures).to.deep.equal(
  381. this.owner.features
  382. )
  383. })
  384. describe('when there is no owner', function () {
  385. beforeEach(function () {
  386. // remove the owner from members list
  387. this.membersWithoutOwner = this.members.filter(
  388. m => m.user._id !== this.owner._id
  389. )
  390. return (this.result = this.handler.buildOwnerAndMembersViews(
  391. this.membersWithoutOwner
  392. ))
  393. })
  394. it('should produce an object with the right keys', function () {
  395. return expect(this.result).to.have.all.keys([
  396. 'owner',
  397. 'ownerFeatures',
  398. 'members',
  399. ])
  400. })
  401. it('should not separate out an owner', function () {
  402. this.result.members.length.should.equal(this.membersWithoutOwner.length)
  403. return expect(this.result.owner).to.equal(null)
  404. })
  405. it('should not extract the ownerFeatures from the owner object', function () {
  406. return expect(this.result.ownerFeatures).to.equal(null)
  407. })
  408. })
  409. })
  410. })