RoomManagerTests.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. /* eslint-disable
  2. no-return-assign,
  3. no-unused-vars,
  4. promise/param-names,
  5. */
  6. // TODO: This file was created by bulk-decaffeinate.
  7. // Fix any style issues and re-enable lint.
  8. /*
  9. * decaffeinate suggestions:
  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 sinon = require('sinon')
  15. const modulePath = '../../../app/js/RoomManager.js'
  16. const SandboxedModule = require('sandboxed-module')
  17. describe('RoomManager', function () {
  18. beforeEach(function () {
  19. this.project_id = 'project-id-123'
  20. this.doc_id = 'doc-id-456'
  21. this.other_doc_id = 'doc-id-789'
  22. this.client = { namespace: { name: '' }, id: 'first-client' }
  23. this.RoomManager = SandboxedModule.require(modulePath, {
  24. requires: {
  25. '@overleaf/settings': (this.settings = {}),
  26. '@overleaf/metrics': (this.metrics = { gauge: sinon.stub() }),
  27. },
  28. })
  29. this.RoomManager._clientsInRoom = sinon.stub()
  30. this.RoomManager._clientAlreadyInRoom = sinon.stub()
  31. this.RoomEvents = this.RoomManager.eventSource()
  32. sinon.spy(this.RoomEvents, 'emit')
  33. return sinon.spy(this.RoomEvents, 'once')
  34. })
  35. describe('emitOnCompletion', function () {
  36. return describe('when a subscribe errors', function () {
  37. afterEach(function () {
  38. return process.removeListener('unhandledRejection', this.onUnhandled)
  39. })
  40. beforeEach(function (done) {
  41. this.onUnhandled = error => {
  42. this.unhandledError = error
  43. return done(new Error(`unhandledRejection: ${error.message}`))
  44. }
  45. process.on('unhandledRejection', this.onUnhandled)
  46. let reject
  47. const subscribePromise = new Promise((_, r) => (reject = r))
  48. const promises = [subscribePromise]
  49. const eventName = 'project-subscribed-123'
  50. this.RoomEvents.once(eventName, () => setTimeout(done, 100))
  51. this.RoomManager.emitOnCompletion(promises, eventName)
  52. return setTimeout(() => reject(new Error('subscribe failed')))
  53. })
  54. return it('should keep going', function () {
  55. return expect(this.unhandledError).to.not.exist
  56. })
  57. })
  58. })
  59. describe('joinProject', function () {
  60. describe('when the project room is empty', function () {
  61. beforeEach(function (done) {
  62. this.RoomManager._clientsInRoom
  63. .withArgs(this.client, this.project_id)
  64. .onFirstCall()
  65. .returns(0)
  66. this.client.join = sinon.stub()
  67. this.callback = sinon.stub()
  68. this.RoomEvents.on('project-active', id => {
  69. return setTimeout(() => {
  70. return this.RoomEvents.emit(`project-subscribed-${id}`)
  71. }, 100)
  72. })
  73. return this.RoomManager.joinProject(
  74. this.client,
  75. this.project_id,
  76. err => {
  77. this.callback(err)
  78. return done()
  79. }
  80. )
  81. })
  82. it("should emit a 'project-active' event with the id", function () {
  83. return this.RoomEvents.emit
  84. .calledWithExactly('project-active', this.project_id)
  85. .should.equal(true)
  86. })
  87. it("should listen for the 'project-subscribed-id' event", function () {
  88. return this.RoomEvents.once
  89. .calledWith(`project-subscribed-${this.project_id}`)
  90. .should.equal(true)
  91. })
  92. return it('should join the room using the id', function () {
  93. return this.client.join
  94. .calledWithExactly(this.project_id)
  95. .should.equal(true)
  96. })
  97. })
  98. return describe('when there are other clients in the project room', function () {
  99. beforeEach(function (done) {
  100. this.RoomManager._clientsInRoom
  101. .withArgs(this.client, this.project_id)
  102. .onFirstCall()
  103. .returns(123)
  104. .onSecondCall()
  105. .returns(124)
  106. this.client.join = sinon.stub()
  107. this.RoomManager.joinProject(this.client, this.project_id, done)
  108. })
  109. it('should join the room using the id', function () {
  110. return this.client.join.called.should.equal(true)
  111. })
  112. return it('should not emit any events', function () {
  113. return this.RoomEvents.emit.called.should.equal(false)
  114. })
  115. })
  116. })
  117. describe('joinDoc', function () {
  118. describe('when the doc room is empty', function () {
  119. beforeEach(function (done) {
  120. this.RoomManager._clientsInRoom
  121. .withArgs(this.client, this.doc_id)
  122. .onFirstCall()
  123. .returns(0)
  124. this.client.join = sinon.stub()
  125. this.callback = sinon.stub()
  126. this.RoomEvents.on('doc-active', id => {
  127. return setTimeout(() => {
  128. return this.RoomEvents.emit(`doc-subscribed-${id}`)
  129. }, 100)
  130. })
  131. return this.RoomManager.joinDoc(this.client, this.doc_id, err => {
  132. this.callback(err)
  133. return done()
  134. })
  135. })
  136. it("should emit a 'doc-active' event with the id", function () {
  137. return this.RoomEvents.emit
  138. .calledWithExactly('doc-active', this.doc_id)
  139. .should.equal(true)
  140. })
  141. it("should listen for the 'doc-subscribed-id' event", function () {
  142. return this.RoomEvents.once
  143. .calledWith(`doc-subscribed-${this.doc_id}`)
  144. .should.equal(true)
  145. })
  146. return it('should join the room using the id', function () {
  147. return this.client.join
  148. .calledWithExactly(this.doc_id)
  149. .should.equal(true)
  150. })
  151. })
  152. return describe('when there are other clients in the doc room', function () {
  153. beforeEach(function (done) {
  154. this.RoomManager._clientsInRoom
  155. .withArgs(this.client, this.doc_id)
  156. .onFirstCall()
  157. .returns(123)
  158. .onSecondCall()
  159. .returns(124)
  160. this.client.join = sinon.stub()
  161. this.RoomManager.joinDoc(this.client, this.doc_id, done)
  162. })
  163. it('should join the room using the id', function () {
  164. return this.client.join.called.should.equal(true)
  165. })
  166. return it('should not emit any events', function () {
  167. return this.RoomEvents.emit.called.should.equal(false)
  168. })
  169. })
  170. })
  171. describe('leaveDoc', function () {
  172. describe('when doc room will be empty after this client has left', function () {
  173. beforeEach(function () {
  174. this.RoomManager._clientAlreadyInRoom
  175. .withArgs(this.client, this.doc_id)
  176. .returns(true)
  177. this.RoomManager._clientsInRoom
  178. .withArgs(this.client, this.doc_id)
  179. .onCall(0)
  180. .returns(0)
  181. this.client.leave = sinon.stub()
  182. return this.RoomManager.leaveDoc(this.client, this.doc_id)
  183. })
  184. it('should leave the room using the id', function () {
  185. return this.client.leave
  186. .calledWithExactly(this.doc_id)
  187. .should.equal(true)
  188. })
  189. return it("should emit a 'doc-empty' event with the id", function () {
  190. return this.RoomEvents.emit
  191. .calledWithExactly('doc-empty', this.doc_id)
  192. .should.equal(true)
  193. })
  194. })
  195. describe('when there are other clients in the doc room', function () {
  196. beforeEach(function () {
  197. this.RoomManager._clientAlreadyInRoom
  198. .withArgs(this.client, this.doc_id)
  199. .returns(true)
  200. this.RoomManager._clientsInRoom
  201. .withArgs(this.client, this.doc_id)
  202. .onCall(0)
  203. .returns(123)
  204. this.client.leave = sinon.stub()
  205. return this.RoomManager.leaveDoc(this.client, this.doc_id)
  206. })
  207. it('should leave the room using the id', function () {
  208. return this.client.leave
  209. .calledWithExactly(this.doc_id)
  210. .should.equal(true)
  211. })
  212. return it('should not emit any events', function () {
  213. return this.RoomEvents.emit.called.should.equal(false)
  214. })
  215. })
  216. return describe('when the client is not in the doc room', function () {
  217. beforeEach(function () {
  218. this.RoomManager._clientAlreadyInRoom
  219. .withArgs(this.client, this.doc_id)
  220. .returns(false)
  221. this.RoomManager._clientsInRoom
  222. .withArgs(this.client, this.doc_id)
  223. .onCall(0)
  224. .returns(0)
  225. this.client.leave = sinon.stub()
  226. return this.RoomManager.leaveDoc(this.client, this.doc_id)
  227. })
  228. it('should not leave the room', function () {
  229. return this.client.leave.called.should.equal(false)
  230. })
  231. return it('should not emit any events', function () {
  232. return this.RoomEvents.emit.called.should.equal(false)
  233. })
  234. })
  235. })
  236. return describe('leaveProjectAndDocs', function () {
  237. return describe('when the client is connected to the project and multiple docs', function () {
  238. beforeEach(function () {
  239. this.RoomManager._roomsClientIsIn = sinon
  240. .stub()
  241. .returns([this.project_id, this.doc_id, this.other_doc_id])
  242. this.client.join = sinon.stub()
  243. return (this.client.leave = sinon.stub())
  244. })
  245. describe('when this is the only client connected', function () {
  246. beforeEach(function (done) {
  247. // first call is for the join,
  248. // second for the leave
  249. this.RoomManager._clientsInRoom
  250. .withArgs(this.client, this.doc_id)
  251. .onCall(0)
  252. .returns(0)
  253. .onCall(1)
  254. .returns(0)
  255. this.RoomManager._clientsInRoom
  256. .withArgs(this.client, this.other_doc_id)
  257. .onCall(0)
  258. .returns(0)
  259. .onCall(1)
  260. .returns(0)
  261. this.RoomManager._clientsInRoom
  262. .withArgs(this.client, this.project_id)
  263. .onCall(0)
  264. .returns(0)
  265. .onCall(1)
  266. .returns(0)
  267. this.RoomManager._clientAlreadyInRoom
  268. .withArgs(this.client, this.doc_id)
  269. .returns(true)
  270. .withArgs(this.client, this.other_doc_id)
  271. .returns(true)
  272. .withArgs(this.client, this.project_id)
  273. .returns(true)
  274. this.RoomEvents.on('project-active', id => {
  275. return setTimeout(() => {
  276. return this.RoomEvents.emit(`project-subscribed-${id}`)
  277. }, 100)
  278. })
  279. this.RoomEvents.on('doc-active', id => {
  280. return setTimeout(() => {
  281. return this.RoomEvents.emit(`doc-subscribed-${id}`)
  282. }, 100)
  283. })
  284. // put the client in the rooms
  285. return this.RoomManager.joinProject(
  286. this.client,
  287. this.project_id,
  288. () => {
  289. return this.RoomManager.joinDoc(this.client, this.doc_id, () => {
  290. return this.RoomManager.joinDoc(
  291. this.client,
  292. this.other_doc_id,
  293. () => {
  294. // now leave the project
  295. this.RoomManager.leaveProjectAndDocs(this.client)
  296. return done()
  297. }
  298. )
  299. })
  300. }
  301. )
  302. })
  303. it('should leave all the docs', function () {
  304. this.client.leave.calledWithExactly(this.doc_id).should.equal(true)
  305. return this.client.leave
  306. .calledWithExactly(this.other_doc_id)
  307. .should.equal(true)
  308. })
  309. it('should leave the project', function () {
  310. return this.client.leave
  311. .calledWithExactly(this.project_id)
  312. .should.equal(true)
  313. })
  314. it("should emit a 'doc-empty' event with the id for each doc", function () {
  315. this.RoomEvents.emit
  316. .calledWithExactly('doc-empty', this.doc_id)
  317. .should.equal(true)
  318. return this.RoomEvents.emit
  319. .calledWithExactly('doc-empty', this.other_doc_id)
  320. .should.equal(true)
  321. })
  322. return it("should emit a 'project-empty' event with the id for the project", function () {
  323. return this.RoomEvents.emit
  324. .calledWithExactly('project-empty', this.project_id)
  325. .should.equal(true)
  326. })
  327. })
  328. return describe('when other clients are still connected', function () {
  329. beforeEach(function () {
  330. this.RoomManager._clientsInRoom
  331. .withArgs(this.client, this.doc_id)
  332. .onFirstCall()
  333. .returns(123)
  334. .onSecondCall()
  335. .returns(122)
  336. this.RoomManager._clientsInRoom
  337. .withArgs(this.client, this.other_doc_id)
  338. .onFirstCall()
  339. .returns(123)
  340. .onSecondCall()
  341. .returns(122)
  342. this.RoomManager._clientsInRoom
  343. .withArgs(this.client, this.project_id)
  344. .onFirstCall()
  345. .returns(123)
  346. .onSecondCall()
  347. .returns(122)
  348. this.RoomManager._clientAlreadyInRoom
  349. .withArgs(this.client, this.doc_id)
  350. .returns(true)
  351. .withArgs(this.client, this.other_doc_id)
  352. .returns(true)
  353. .withArgs(this.client, this.project_id)
  354. .returns(true)
  355. return this.RoomManager.leaveProjectAndDocs(this.client)
  356. })
  357. it('should leave all the docs', function () {
  358. this.client.leave.calledWithExactly(this.doc_id).should.equal(true)
  359. return this.client.leave
  360. .calledWithExactly(this.other_doc_id)
  361. .should.equal(true)
  362. })
  363. it('should leave the project', function () {
  364. return this.client.leave
  365. .calledWithExactly(this.project_id)
  366. .should.equal(true)
  367. })
  368. return it('should not emit any events', function () {
  369. return this.RoomEvents.emit.called.should.equal(false)
  370. })
  371. })
  372. })
  373. })
  374. })