ReceiveEditorEventTests.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /* eslint-disable
  2. no-unused-vars,
  3. */
  4. // TODO: This file was created by bulk-decaffeinate.
  5. // Fix any style issues and re-enable lint.
  6. /*
  7. * decaffeinate suggestions:
  8. * DS102: Remove unnecessary code created because of implicit returns
  9. * DS207: Consider shorter variations of null checks
  10. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  11. */
  12. const { expect } = require('chai')
  13. const RealTimeClient = require('./helpers/RealTimeClient')
  14. const FixturesManager = require('./helpers/FixturesManager')
  15. const async = require('async')
  16. const settings = require('@overleaf/settings')
  17. const redis = require('@overleaf/redis-wrapper')
  18. const rclient = redis.createClient(settings.redis.pubsub)
  19. describe('receiveEditorEvent', function () {
  20. beforeEach(function (done) {
  21. this.lines = ['test', 'doc', 'lines']
  22. this.version = 42
  23. this.ops = ['mock', 'doc', 'ops']
  24. /**
  25. * We will set up a project, a doc, and three users: the owner, user 'a' and user 'b'
  26. */
  27. this.project_id = null
  28. this.doc_id = null
  29. this.owner_user_id = null
  30. this.owner_client = null
  31. this.user_a_id = null
  32. this.user_a_client = null
  33. this.user_b_id = null
  34. this.user_b_client = null
  35. this.user_c_id = null
  36. this.user_c_client = null
  37. async.series(
  38. [
  39. /**
  40. * Create the project, doc, and owner
  41. */
  42. cb => {
  43. return FixturesManager.setUpProject(
  44. {
  45. privilegeLevel: 'owner',
  46. project: { name: 'Test Project' },
  47. userMetadata: { isInvitedMember: true },
  48. },
  49. (error, { user_id: userId, project_id: projectId }) => {
  50. if (error) return done(error)
  51. this.owner_user_id = userId
  52. this.project_id = projectId
  53. return cb()
  54. }
  55. )
  56. },
  57. cb => {
  58. return FixturesManager.setUpDoc(
  59. this.project_id,
  60. { lines: this.lines, version: this.version, ops: this.ops },
  61. (e, { doc_id: docId }) => {
  62. this.doc_id = docId
  63. return cb(e)
  64. }
  65. )
  66. },
  67. /**
  68. * Connect owner to project/doc
  69. */
  70. cb => {
  71. this.owner_client = RealTimeClient.connect()
  72. return this.owner_client.on('connectionAccepted', cb)
  73. },
  74. cb => {
  75. return this.owner_client.emit(
  76. 'joinProject',
  77. {
  78. project_id: this.project_id,
  79. },
  80. cb
  81. )
  82. },
  83. cb => {
  84. return this.owner_client.emit('joinDoc', this.doc_id, cb)
  85. },
  86. /**
  87. * add user_a to project, as an invited member
  88. */
  89. cb => {
  90. return FixturesManager.setUpProject(
  91. {
  92. privilegeLevel: 'readAndWrite',
  93. project_id: this.project_id,
  94. userMetadata: { isTokenMember: false, isInvitedMember: true },
  95. },
  96. (error, { user_id: userIdSecond }) => {
  97. if (error) return done(error)
  98. this.user_a_id = userIdSecond
  99. return cb()
  100. }
  101. )
  102. },
  103. /**
  104. * Connect user_a to project/doc
  105. */
  106. cb => {
  107. this.user_a_client = RealTimeClient.connect()
  108. return this.user_a_client.on('connectionAccepted', cb)
  109. },
  110. cb => {
  111. return this.user_a_client.emit(
  112. 'joinProject',
  113. {
  114. project_id: this.project_id,
  115. },
  116. cb
  117. )
  118. },
  119. cb => {
  120. return this.user_a_client.emit('joinDoc', this.doc_id, cb)
  121. },
  122. /**
  123. * Set up user_b, as a token-access/link-sharing user
  124. */
  125. cb => {
  126. return FixturesManager.setUpProject(
  127. {
  128. privilegeLevel: 'readAndWrite',
  129. project_id: this.project_id,
  130. userMetadata: { isTokenMember: true, isInvitedMember: false },
  131. },
  132. (error, { user_id: userIdThird }) => {
  133. if (error) return done(error)
  134. this.user_b_id = userIdThird
  135. return cb()
  136. }
  137. )
  138. },
  139. /**
  140. * Connect user_b to project/doc
  141. */
  142. cb => {
  143. this.user_b_client = RealTimeClient.connect()
  144. return this.user_b_client.on('connectionAccepted', cb)
  145. },
  146. cb => {
  147. return this.user_b_client.emit(
  148. 'joinProject',
  149. {
  150. project_id: this.project_id,
  151. },
  152. cb
  153. )
  154. },
  155. cb => {
  156. return this.user_b_client.emit('joinDoc', this.doc_id, cb)
  157. },
  158. /**
  159. * Set up user_c, as a 'restricted' user (anonymous read-only link-sharing)
  160. */
  161. cb => {
  162. return FixturesManager.setUpProject(
  163. {
  164. privilegeLevel: 'readAndWrite',
  165. project_id: this.project_id,
  166. userMetadata: {
  167. isTokenMember: false,
  168. isInvitedMember: false,
  169. isRestrictedUser: true,
  170. },
  171. },
  172. (error, { user_id: userIdFourth }) => {
  173. if (error) return done(error)
  174. this.user_c_id = userIdFourth
  175. return cb()
  176. }
  177. )
  178. },
  179. /**
  180. * Connect user_c to project/doc
  181. */
  182. cb => {
  183. this.user_c_client = RealTimeClient.connect()
  184. return this.user_c_client.on('connectionAccepted', cb)
  185. },
  186. cb => {
  187. return this.user_c_client.emit(
  188. 'joinProject',
  189. {
  190. project_id: this.project_id,
  191. },
  192. cb
  193. )
  194. },
  195. cb => {
  196. return this.user_c_client.emit('joinDoc', this.doc_id, cb)
  197. },
  198. // --------------
  199. /**
  200. * Listen for updates
  201. */
  202. cb => {
  203. this.owner_updates = []
  204. this.user_a_updates = []
  205. this.user_b_updates = []
  206. this.user_c_updates = []
  207. const eventNames = [
  208. 'userRemovedFromProject',
  209. 'project:publicAccessLevel:changed',
  210. 'project:access:revoked',
  211. ]
  212. for (const eventName of eventNames) {
  213. this.owner_client.on(eventName, update =>
  214. this.owner_updates.push({ [eventName]: update })
  215. )
  216. this.user_a_client.on(eventName, update =>
  217. this.user_a_updates.push({ [eventName]: update })
  218. )
  219. this.user_b_client.on(eventName, update =>
  220. this.user_b_updates.push({ [eventName]: update })
  221. )
  222. this.user_c_client.on(eventName, update =>
  223. this.user_c_updates.push({ [eventName]: update })
  224. )
  225. }
  226. return cb()
  227. },
  228. ],
  229. done
  230. )
  231. })
  232. afterEach(function () {
  233. if (this.owner_client) {
  234. this.owner_client.disconnect()
  235. }
  236. if (this.user_a_client) {
  237. this.user_a_client.disconnect()
  238. }
  239. if (this.user_b_client) {
  240. this.user_b_client.disconnect()
  241. }
  242. if (this.user_c_client) {
  243. this.user_c_client.disconnect()
  244. }
  245. })
  246. describe('event: project:publicAccessLevel:changed, set to private', function () {
  247. beforeEach(function (done) {
  248. /**
  249. * We turn off link sharing
  250. */
  251. rclient.publish(
  252. 'editor-events',
  253. JSON.stringify({
  254. room_id: this.project_id,
  255. message: 'project:publicAccessLevel:changed',
  256. payload: [{ newAccessLevel: 'private' }],
  257. })
  258. )
  259. setTimeout(done, 200)
  260. })
  261. it('should disconnect the token-access user, and restricted users', function () {
  262. expect(this.user_b_client.socket.connected).to.equal(false)
  263. expect(this.user_c_client.socket.connected).to.equal(false)
  264. })
  265. it('should not disconnect the other users', function () {
  266. expect(this.owner_client.socket.connected).to.equal(true)
  267. expect(this.user_a_client.socket.connected).to.equal(true)
  268. })
  269. it('should send the event to the remaining connected clients', function () {
  270. expect(this.owner_updates).to.deep.equal([
  271. { 'project:publicAccessLevel:changed': { newAccessLevel: 'private' } },
  272. ])
  273. expect(this.user_a_updates).to.deep.equal([
  274. { 'project:publicAccessLevel:changed': { newAccessLevel: 'private' } },
  275. ])
  276. })
  277. it('should send a project:access:revoked message to the disconnected clients', function () {
  278. expect(this.user_b_updates).to.deep.equal([
  279. { 'project:access:revoked': undefined },
  280. ])
  281. expect(this.user_c_updates).to.deep.equal([
  282. { 'project:access:revoked': undefined },
  283. ])
  284. })
  285. })
  286. describe('event: project:publicAccessLevel:changed, set to tokenBased', function () {
  287. beforeEach(function (done) {
  288. /**
  289. * We turn on link sharing
  290. */
  291. rclient.publish(
  292. 'editor-events',
  293. JSON.stringify({
  294. room_id: this.project_id,
  295. message: 'project:publicAccessLevel:changed',
  296. payload: [{ newAccessLevel: 'tokenBased' }],
  297. })
  298. )
  299. setTimeout(done, 200)
  300. })
  301. it('should not disconnect anyone', function () {
  302. expect(this.owner_client.socket.connected).to.equal(true)
  303. expect(this.user_a_client.socket.connected).to.equal(true)
  304. expect(this.user_b_client.socket.connected).to.equal(true)
  305. expect(this.user_c_client.socket.connected).to.equal(true)
  306. })
  307. it('should send the event to all non-restricted clients', function () {
  308. expect(this.owner_updates).to.deep.equal([
  309. {
  310. 'project:publicAccessLevel:changed': { newAccessLevel: 'tokenBased' },
  311. },
  312. ])
  313. expect(this.user_a_updates).to.deep.equal([
  314. {
  315. 'project:publicAccessLevel:changed': { newAccessLevel: 'tokenBased' },
  316. },
  317. ])
  318. expect(this.user_b_updates).to.deep.equal([
  319. {
  320. 'project:publicAccessLevel:changed': { newAccessLevel: 'tokenBased' },
  321. },
  322. ])
  323. // restricted users don't receive this type of message
  324. expect(this.user_c_updates.length).to.equal(0)
  325. })
  326. })
  327. describe('event: userRemovedFromProject', function () {
  328. let removedUserId
  329. beforeEach(function (done) {
  330. /**
  331. * We remove user_a from the project
  332. */
  333. removedUserId = `${this.user_a_id}`
  334. rclient.publish(
  335. 'editor-events',
  336. JSON.stringify({
  337. room_id: this.project_id,
  338. message: 'userRemovedFromProject',
  339. payload: [removedUserId],
  340. })
  341. )
  342. setTimeout(done, 200)
  343. })
  344. it('should disconnect the removed user', function () {
  345. expect(this.user_a_client.socket.connected).to.equal(false)
  346. })
  347. it('should not disconnect the other users', function () {
  348. expect(this.owner_client.socket.connected).to.equal(true)
  349. expect(this.user_b_client.socket.connected).to.equal(true)
  350. })
  351. it('should send the event to the remaining connected clients', function () {
  352. expect(this.owner_updates).to.deep.equal([
  353. { userRemovedFromProject: removedUserId },
  354. ])
  355. expect(this.user_b_updates).to.deep.equal([
  356. { userRemovedFromProject: removedUserId },
  357. ])
  358. })
  359. it('should send a project:access:revoked message to the disconnected clients', function () {
  360. expect(this.user_a_updates).to.deep.equal([
  361. { 'project:access:revoked': undefined },
  362. ])
  363. })
  364. })
  365. })