ReceiveEditorEventTests.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  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. 'project:tokens:changed',
  212. ]
  213. for (const eventName of eventNames) {
  214. this.owner_client.on(eventName, update =>
  215. this.owner_updates.push({ [eventName]: update })
  216. )
  217. this.user_a_client.on(eventName, update =>
  218. this.user_a_updates.push({ [eventName]: update })
  219. )
  220. this.user_b_client.on(eventName, update =>
  221. this.user_b_updates.push({ [eventName]: update })
  222. )
  223. this.user_c_client.on(eventName, update =>
  224. this.user_c_updates.push({ [eventName]: update })
  225. )
  226. }
  227. return cb()
  228. },
  229. ],
  230. done
  231. )
  232. })
  233. afterEach(function () {
  234. if (this.owner_client) {
  235. this.owner_client.disconnect()
  236. }
  237. if (this.user_a_client) {
  238. this.user_a_client.disconnect()
  239. }
  240. if (this.user_b_client) {
  241. this.user_b_client.disconnect()
  242. }
  243. if (this.user_c_client) {
  244. this.user_c_client.disconnect()
  245. }
  246. })
  247. describe('event: project:tokens:changed', function () {
  248. beforeEach(function (done) {
  249. rclient.publish(
  250. 'editor-events',
  251. JSON.stringify({
  252. room_id: this.project_id,
  253. message: 'project:tokens:changed',
  254. payload: [{ tokens: 'TOKENS' }],
  255. })
  256. )
  257. setTimeout(done, 200)
  258. })
  259. it('should send the event to the owner', function () {
  260. expect(this.owner_updates).to.deep.equal([
  261. { 'project:tokens:changed': { tokens: 'TOKENS' } },
  262. ])
  263. })
  264. it('should not send the event to the other clients', function () {
  265. expect(this.user_a_updates).to.deep.equal([])
  266. expect(this.user_b_updates).to.deep.equal([])
  267. expect(this.user_c_updates).to.deep.equal([])
  268. })
  269. })
  270. describe('event: project:publicAccessLevel:changed, set to private', function () {
  271. beforeEach(function (done) {
  272. /**
  273. * We turn off link sharing
  274. */
  275. rclient.publish(
  276. 'editor-events',
  277. JSON.stringify({
  278. room_id: this.project_id,
  279. message: 'project:publicAccessLevel:changed',
  280. payload: [{ newAccessLevel: 'private' }],
  281. })
  282. )
  283. setTimeout(done, 200)
  284. })
  285. it('should disconnect the token-access user, and restricted users', function () {
  286. expect(this.user_b_client.socket.connected).to.equal(false)
  287. expect(this.user_c_client.socket.connected).to.equal(false)
  288. })
  289. it('should not disconnect the other users', function () {
  290. expect(this.owner_client.socket.connected).to.equal(true)
  291. expect(this.user_a_client.socket.connected).to.equal(true)
  292. })
  293. it('should send the event to the remaining connected clients', function () {
  294. expect(this.owner_updates).to.deep.equal([
  295. { 'project:publicAccessLevel:changed': { newAccessLevel: 'private' } },
  296. ])
  297. expect(this.user_a_updates).to.deep.equal([
  298. { 'project:publicAccessLevel:changed': { newAccessLevel: 'private' } },
  299. ])
  300. })
  301. it('should send a project:access:revoked message to the disconnected clients', function () {
  302. expect(this.user_b_updates).to.deep.equal([
  303. { 'project:access:revoked': undefined },
  304. ])
  305. expect(this.user_c_updates).to.deep.equal([
  306. { 'project:access:revoked': undefined },
  307. ])
  308. })
  309. })
  310. describe('event: project:publicAccessLevel:changed, set to tokenBased', function () {
  311. beforeEach(function (done) {
  312. /**
  313. * We turn on link sharing
  314. */
  315. rclient.publish(
  316. 'editor-events',
  317. JSON.stringify({
  318. room_id: this.project_id,
  319. message: 'project:publicAccessLevel:changed',
  320. payload: [{ newAccessLevel: 'tokenBased' }],
  321. })
  322. )
  323. setTimeout(done, 200)
  324. })
  325. it('should not disconnect anyone', function () {
  326. expect(this.owner_client.socket.connected).to.equal(true)
  327. expect(this.user_a_client.socket.connected).to.equal(true)
  328. expect(this.user_b_client.socket.connected).to.equal(true)
  329. expect(this.user_c_client.socket.connected).to.equal(true)
  330. })
  331. it('should send the event to all non-restricted clients', function () {
  332. expect(this.owner_updates).to.deep.equal([
  333. {
  334. 'project:publicAccessLevel:changed': { newAccessLevel: 'tokenBased' },
  335. },
  336. ])
  337. expect(this.user_a_updates).to.deep.equal([
  338. {
  339. 'project:publicAccessLevel:changed': { newAccessLevel: 'tokenBased' },
  340. },
  341. ])
  342. expect(this.user_b_updates).to.deep.equal([
  343. {
  344. 'project:publicAccessLevel:changed': { newAccessLevel: 'tokenBased' },
  345. },
  346. ])
  347. // restricted users don't receive this type of message
  348. expect(this.user_c_updates.length).to.equal(0)
  349. })
  350. })
  351. describe('event: userRemovedFromProject', function () {
  352. let removedUserId
  353. beforeEach(function (done) {
  354. /**
  355. * We remove user_a from the project
  356. */
  357. removedUserId = `${this.user_a_id}`
  358. rclient.publish(
  359. 'editor-events',
  360. JSON.stringify({
  361. room_id: this.project_id,
  362. message: 'userRemovedFromProject',
  363. payload: [removedUserId],
  364. })
  365. )
  366. setTimeout(done, 200)
  367. })
  368. it('should disconnect the removed user', function () {
  369. expect(this.user_a_client.socket.connected).to.equal(false)
  370. })
  371. it('should not disconnect the other users', function () {
  372. expect(this.owner_client.socket.connected).to.equal(true)
  373. expect(this.user_b_client.socket.connected).to.equal(true)
  374. })
  375. it('should send the event to the remaining connected clients', function () {
  376. expect(this.owner_updates).to.deep.equal([
  377. { userRemovedFromProject: removedUserId },
  378. ])
  379. expect(this.user_b_updates).to.deep.equal([
  380. { userRemovedFromProject: removedUserId },
  381. ])
  382. })
  383. it('should send a project:access:revoked message to the disconnected clients', function () {
  384. expect(this.user_a_updates).to.deep.equal([
  385. { 'project:access:revoked': undefined },
  386. ])
  387. })
  388. })
  389. })