Sfoglia il codice sorgente

decaffeinate: Convert ApplyUpdateTests.coffee and 18 other files to JS

decaffeinate 6 anni fa
parent
commit
d318e4fd0e

+ 282 - 187
services/real-time/test/acceptance/coffee/ApplyUpdateTests.js

@@ -1,222 +1,317 @@
-async = require "async"
-chai = require("chai")
-expect = chai.expect
-chai.should()
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS201: Simplify complex destructure assignments
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const async = require("async");
+const chai = require("chai");
+const {
+    expect
+} = chai;
+chai.should();
 
-RealTimeClient = require "./helpers/RealTimeClient"
-FixturesManager = require "./helpers/FixturesManager"
+const RealTimeClient = require("./helpers/RealTimeClient");
+const FixturesManager = require("./helpers/FixturesManager");
 
-settings = require "settings-sharelatex"
-redis = require "redis-sharelatex"
-rclient = redis.createClient(settings.redis.documentupdater)
+const settings = require("settings-sharelatex");
+const redis = require("redis-sharelatex");
+const rclient = redis.createClient(settings.redis.documentupdater);
 
-redisSettings = settings.redis
+const redisSettings = settings.redis;
 
-describe "applyOtUpdate", ->
-	before ->
-		@update = {
+describe("applyOtUpdate", function() {
+	before(function() {
+		return this.update = {
 			op: [{i: "foo", p: 42}]
-		}
-	describe "when authorized", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
+		};});
+	describe("when authorized", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
 						privilegeLevel: "readAndWrite"
-					}, (e, {@project_id, @user_id}) =>
-						cb(e)
+					}, (e, {project_id, user_id}) => {
+						this.project_id = project_id;
+						this.user_id = user_id;
+						return cb(e);
+					});
+				},
 					
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-						cb(e)
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
 
-				(cb) =>
-					@client = RealTimeClient.connect()
-					@client.on "connectionAccepted", cb
+				cb => {
+					this.client = RealTimeClient.connect();
+					return this.client.on("connectionAccepted", cb);
+				},
 						
-				(cb) =>
-					@client.emit "joinProject", project_id: @project_id, cb
+				cb => {
+					return this.client.emit("joinProject", {project_id: this.project_id}, cb);
+				},
 				
-				(cb) =>
-					@client.emit "joinDoc", @doc_id, cb
+				cb => {
+					return this.client.emit("joinDoc", this.doc_id, cb);
+				},
 					
-				(cb) =>
-					@client.emit "applyOtUpdate", @doc_id, @update, cb
-			], done
-		
-		it "should push the doc into the pending updates list", (done) ->
-			rclient.lrange "pending-updates-list", 0, -1, (error, [doc_id]) =>
-				doc_id.should.equal "#{@project_id}:#{@doc_id}"
-				done()
-			return null
-
-		it "should push the update into redis", (done) ->
-			rclient.lrange redisSettings.documentupdater.key_schema.pendingUpdates({@doc_id}), 0, -1, (error, [update]) =>
-				update = JSON.parse(update)
-				update.op.should.deep.equal @update.op
-				update.meta.should.deep.equal {
-					source: @client.publicId
-					user_id: @user_id
+				cb => {
+					return this.client.emit("applyOtUpdate", this.doc_id, this.update, cb);
 				}
-				done()
-			return null
-
-		after (done) ->
-			async.series [
-				(cb) => rclient.del "pending-updates-list", cb
-				(cb) => rclient.del "DocsWithPendingUpdates", "#{@project_id}:#{@doc_id}", cb
-				(cb) => rclient.del redisSettings.documentupdater.key_schema.pendingUpdates(@doc_id), cb
-			], done
+			], done);
+		});
+		
+		it("should push the doc into the pending updates list", function(done) {
+			rclient.lrange("pending-updates-list", 0, -1, (error, ...rest) => {
+				const [doc_id] = Array.from(rest[0]);
+				doc_id.should.equal(`${this.project_id}:${this.doc_id}`);
+				return done();
+			});
+			return null;
+		});
+
+		it("should push the update into redis", function(done) {
+			rclient.lrange(redisSettings.documentupdater.key_schema.pendingUpdates({doc_id: this.doc_id}), 0, -1, (error, ...rest) => {
+				let [update] = Array.from(rest[0]);
+				update = JSON.parse(update);
+				update.op.should.deep.equal(this.update.op);
+				update.meta.should.deep.equal({
+					source: this.client.publicId,
+					user_id: this.user_id
+				});
+				return done();
+			});
+			return null;
+		});
+
+		return after(function(done) {
+			return async.series([
+				cb => rclient.del("pending-updates-list", cb),
+				cb => rclient.del("DocsWithPendingUpdates", `${this.project_id}:${this.doc_id}`, cb),
+				cb => rclient.del(redisSettings.documentupdater.key_schema.pendingUpdates(this.doc_id), cb)
+			], done);
+		});
+	});
 		
-	describe "when authorized with a huge edit update", ->
-		before (done) ->
-			@update = {
+	describe("when authorized with a huge edit update", function() {
+		before(function(done) {
+			this.update = {
 				op: {
 					p: 12,
-					t: "update is too large".repeat(1024 * 400) # >7MB
+					t: "update is too large".repeat(1024 * 400) // >7MB
 				}
-			}
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
+			};
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
 						privilegeLevel: "readAndWrite"
-					}, (e, {@project_id, @user_id}) =>
-						cb(e)
-
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-						cb(e)
-
-				(cb) =>
-					@client = RealTimeClient.connect()
-					@client.on "connectionAccepted", cb
-					@client.on "otUpdateError", (@otUpdateError) =>
-
-				(cb) =>
-					@client.emit "joinProject", project_id: @project_id, cb
-
-				(cb) =>
-					@client.emit "joinDoc", @doc_id, cb
-
-				(cb) =>
-					@client.emit "applyOtUpdate", @doc_id, @update, (@error) =>
-						cb()
-			], done
-
-		it "should not return an error", ->
-			expect(@error).to.not.exist
-
-		it "should send an otUpdateError to the client", (done) ->
-			setTimeout () =>
-				expect(@otUpdateError).to.exist
-				done()
-			, 300
-
-		it "should disconnect the client", (done) ->
-			setTimeout () =>
-				@client.socket.connected.should.equal false
-				done()
-			, 300
-
-		it "should not put the update in redis", (done) ->
-			rclient.llen redisSettings.documentupdater.key_schema.pendingUpdates({@doc_id}), (error, len) =>
-				len.should.equal 0
-				done()
-			return null
-
-	describe "when authorized to read-only with an edit update", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
+					}, (e, {project_id, user_id}) => {
+						this.project_id = project_id;
+						this.user_id = user_id;
+						return cb(e);
+					});
+				},
+
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
+
+				cb => {
+					this.client = RealTimeClient.connect();
+					this.client.on("connectionAccepted", cb);
+					return this.client.on("otUpdateError", otUpdateError => {
+						this.otUpdateError = otUpdateError;
+						
+				});
+				},
+
+				cb => {
+					return this.client.emit("joinProject", {project_id: this.project_id}, cb);
+				},
+
+				cb => {
+					return this.client.emit("joinDoc", this.doc_id, cb);
+				},
+
+				cb => {
+					return this.client.emit("applyOtUpdate", this.doc_id, this.update, error => {
+						this.error = error;
+						return cb();
+					});
+				}
+			], done);
+		});
+
+		it("should not return an error", function() {
+			return expect(this.error).to.not.exist;
+		});
+
+		it("should send an otUpdateError to the client", function(done) {
+			return setTimeout(() => {
+				expect(this.otUpdateError).to.exist;
+				return done();
+			}
+			, 300);
+		});
+
+		it("should disconnect the client", function(done) {
+			return setTimeout(() => {
+				this.client.socket.connected.should.equal(false);
+				return done();
+			}
+			, 300);
+		});
+
+		return it("should not put the update in redis", function(done) {
+			rclient.llen(redisSettings.documentupdater.key_schema.pendingUpdates({doc_id: this.doc_id}), (error, len) => {
+				len.should.equal(0);
+				return done();
+			});
+			return null;
+		});
+	});
+
+	describe("when authorized to read-only with an edit update", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
 						privilegeLevel: "readOnly"
-					}, (e, {@project_id, @user_id}) =>
-						cb(e)
+					}, (e, {project_id, user_id}) => {
+						this.project_id = project_id;
+						this.user_id = user_id;
+						return cb(e);
+					});
+				},
 					
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-						cb(e)
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
 
-				(cb) =>
-					@client = RealTimeClient.connect()
-					@client.on "connectionAccepted", cb
+				cb => {
+					this.client = RealTimeClient.connect();
+					return this.client.on("connectionAccepted", cb);
+				},
 						
-				(cb) =>
-					@client.emit "joinProject", project_id: @project_id, cb
+				cb => {
+					return this.client.emit("joinProject", {project_id: this.project_id}, cb);
+				},
 				
-				(cb) =>
-					@client.emit "joinDoc", @doc_id, cb
+				cb => {
+					return this.client.emit("joinDoc", this.doc_id, cb);
+				},
 					
-				(cb) =>
-					@client.emit "applyOtUpdate", @doc_id, @update, (@error) =>
-						cb()
-			], done
+				cb => {
+					return this.client.emit("applyOtUpdate", this.doc_id, this.update, error => {
+						this.error = error;
+						return cb();
+					});
+				}
+			], done);
+		});
 		
-		it "should return an error", ->
-			expect(@error).to.exist
+		it("should return an error", function() {
+			return expect(this.error).to.exist;
+		});
 		
-		it "should disconnect the client", (done) ->
-			setTimeout () =>
-				@client.socket.connected.should.equal false
-				done()
-			, 300
+		it("should disconnect the client", function(done) {
+			return setTimeout(() => {
+				this.client.socket.connected.should.equal(false);
+				return done();
+			}
+			, 300);
+		});
 			
-		it "should not put the update in redis", (done) ->
-			rclient.llen redisSettings.documentupdater.key_schema.pendingUpdates({@doc_id}), (error, len) =>
-				len.should.equal 0
-				done()
-			return null
+		return it("should not put the update in redis", function(done) {
+			rclient.llen(redisSettings.documentupdater.key_schema.pendingUpdates({doc_id: this.doc_id}), (error, len) => {
+				len.should.equal(0);
+				return done();
+			});
+			return null;
+		});
+	});
 				
-	describe "when authorized to read-only with a comment update", ->
-		before (done) ->
-			@comment_update = {
+	return describe("when authorized to read-only with a comment update", function() {
+		before(function(done) {
+			this.comment_update = {
 				op: [{c: "foo", p: 42}]
-			}
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
+			};
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
 						privilegeLevel: "readOnly"
-					}, (e, {@project_id, @user_id}) =>
-						cb(e)
+					}, (e, {project_id, user_id}) => {
+						this.project_id = project_id;
+						this.user_id = user_id;
+						return cb(e);
+					});
+				},
 					
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-						cb(e)
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
 
-				(cb) =>
-					@client = RealTimeClient.connect()
-					@client.on "connectionAccepted", cb
+				cb => {
+					this.client = RealTimeClient.connect();
+					return this.client.on("connectionAccepted", cb);
+				},
 						
-				(cb) =>
-					@client.emit "joinProject", project_id: @project_id, cb
+				cb => {
+					return this.client.emit("joinProject", {project_id: this.project_id}, cb);
+				},
 				
-				(cb) =>
-					@client.emit "joinDoc", @doc_id, cb
+				cb => {
+					return this.client.emit("joinDoc", this.doc_id, cb);
+				},
 					
-				(cb) =>
-					@client.emit "applyOtUpdate", @doc_id, @comment_update, cb
-			], done
-		
-		it "should push the doc into the pending updates list", (done) ->
-			rclient.lrange "pending-updates-list", 0, -1, (error, [doc_id]) =>
-				doc_id.should.equal "#{@project_id}:#{@doc_id}"
-				done()
-			return null
-
-		it "should push the update into redis", (done) ->
-			rclient.lrange redisSettings.documentupdater.key_schema.pendingUpdates({@doc_id}), 0, -1, (error, [update]) =>
-				update = JSON.parse(update)
-				update.op.should.deep.equal @comment_update.op
-				update.meta.should.deep.equal {
-					source: @client.publicId
-					user_id: @user_id
+				cb => {
+					return this.client.emit("applyOtUpdate", this.doc_id, this.comment_update, cb);
 				}
-				done()
-			return null
-
-		after (done) ->
-			async.series [
-				(cb) => rclient.del "pending-updates-list", cb
-				(cb) => rclient.del "DocsWithPendingUpdates", "#{@project_id}:#{@doc_id}", cb
-				(cb) => rclient.del redisSettings.documentupdater.key_schema.pendingUpdates({@doc_id}), cb
-			], done
+			], done);
+		});
+		
+		it("should push the doc into the pending updates list", function(done) {
+			rclient.lrange("pending-updates-list", 0, -1, (error, ...rest) => {
+				const [doc_id] = Array.from(rest[0]);
+				doc_id.should.equal(`${this.project_id}:${this.doc_id}`);
+				return done();
+			});
+			return null;
+		});
+
+		it("should push the update into redis", function(done) {
+			rclient.lrange(redisSettings.documentupdater.key_schema.pendingUpdates({doc_id: this.doc_id}), 0, -1, (error, ...rest) => {
+				let [update] = Array.from(rest[0]);
+				update = JSON.parse(update);
+				update.op.should.deep.equal(this.comment_update.op);
+				update.meta.should.deep.equal({
+					source: this.client.publicId,
+					user_id: this.user_id
+				});
+				return done();
+			});
+			return null;
+		});
+
+		return after(function(done) {
+			return async.series([
+				cb => rclient.del("pending-updates-list", cb),
+				cb => rclient.del("DocsWithPendingUpdates", `${this.project_id}:${this.doc_id}`, cb),
+				cb => rclient.del(redisSettings.documentupdater.key_schema.pendingUpdates({doc_id: this.doc_id}), cb)
+			], done);
+		});
+	});
+});

+ 158 - 113
services/real-time/test/acceptance/coffee/ClientTrackingTests.js

@@ -1,146 +1,191 @@
-chai = require("chai")
-expect = chai.expect
-chai.should()
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const chai = require("chai");
+const {
+    expect
+} = chai;
+chai.should();
 
-RealTimeClient = require "./helpers/RealTimeClient"
-MockWebServer = require "./helpers/MockWebServer"
-FixturesManager = require "./helpers/FixturesManager"
+const RealTimeClient = require("./helpers/RealTimeClient");
+const MockWebServer = require("./helpers/MockWebServer");
+const FixturesManager = require("./helpers/FixturesManager");
 
-async = require "async"
+const async = require("async");
 
-describe "clientTracking", ->
-	describe "when a client updates its cursor location", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
-						privilegeLevel: "owner"
+describe("clientTracking", function() {
+	describe("when a client updates its cursor location", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
+						privilegeLevel: "owner",
 						project: { name: "Test Project" }
-					}, (error, {@user_id, @project_id}) => cb()
+					}, (error, {user_id, project_id}) => { this.user_id = user_id; this.project_id = project_id; return cb(); });
+				},
 				
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-						cb(e)
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
 				
-				(cb) =>
-					@clientA = RealTimeClient.connect()
-					@clientA.on "connectionAccepted", cb
+				cb => {
+					this.clientA = RealTimeClient.connect();
+					return this.clientA.on("connectionAccepted", cb);
+				},
 					
-				(cb) =>
-					@clientB = RealTimeClient.connect()
-					@clientB.on "connectionAccepted", cb
+				cb => {
+					this.clientB = RealTimeClient.connect();
+					return this.clientB.on("connectionAccepted", cb);
+				},
 					
-				(cb) =>
-					@clientA.emit "joinProject", {
-						project_id: @project_id
-					}, cb
+				cb => {
+					return this.clientA.emit("joinProject", {
+						project_id: this.project_id
+					}, cb);
+				},
 				
-				(cb) =>
-					@clientA.emit "joinDoc", @doc_id, cb
+				cb => {
+					return this.clientA.emit("joinDoc", this.doc_id, cb);
+				},
 					
-				(cb) =>
-					@clientB.emit "joinProject", {
-						project_id: @project_id
-					}, cb
+				cb => {
+					return this.clientB.emit("joinProject", {
+						project_id: this.project_id
+					}, cb);
+				},
 					
-				(cb) =>
-					@updates = []
-					@clientB.on "clientTracking.clientUpdated", (data) =>
-						@updates.push data
+				cb => {
+					this.updates = [];
+					this.clientB.on("clientTracking.clientUpdated", data => {
+						return this.updates.push(data);
+					});
 
-					@clientA.emit "clientTracking.updatePosition", {
-						row: @row = 42
-						column: @column = 36
-						doc_id: @doc_id
-					}, (error) ->
-						throw error if error?
-						setTimeout cb, 300 # Give the message a chance to reach client B.
-			], done
+					return this.clientA.emit("clientTracking.updatePosition", {
+						row: (this.row = 42),
+						column: (this.column = 36),
+						doc_id: this.doc_id
+					}, function(error) {
+						if (error != null) { throw error; }
+						return setTimeout(cb, 300);
+					});
+				} // Give the message a chance to reach client B.
+			], done);
+		});
 			
-		it "should tell other clients about the update", ->
-			@updates.should.deep.equal [
+		it("should tell other clients about the update", function() {
+			return this.updates.should.deep.equal([
 				{
-					row: @row
-					column: @column
-					doc_id: @doc_id
-					id: @clientA.publicId
-					user_id: @user_id
+					row: this.row,
+					column: this.column,
+					doc_id: this.doc_id,
+					id: this.clientA.publicId,
+					user_id: this.user_id,
 					name: "Joe Bloggs"
 				}
-			]
+			]);
+	});
 		
-		it "should record the update in getConnectedUsers", (done) ->
-			@clientB.emit "clientTracking.getConnectedUsers", (error, users) =>
-				for user in users
-					if user.client_id == @clientA.publicId
+		return it("should record the update in getConnectedUsers", function(done) {
+			return this.clientB.emit("clientTracking.getConnectedUsers", (error, users) => {
+				for (let user of Array.from(users)) {
+					if (user.client_id === this.clientA.publicId) {
 						expect(user.cursorData).to.deep.equal({
-							row: @row
-							column: @column
-							doc_id: @doc_id 
-						})
-						return done()
-				throw new Error("user was never found")
+							row: this.row,
+							column: this.column,
+							doc_id: this.doc_id 
+						});
+						return done();
+					}
+				}
+				throw new Error("user was never found");
+			});
+		});
+	});
 				
-	describe "when an anonymous client updates its cursor location", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
-						privilegeLevel: "owner"
-						project: { name: "Test Project"	}
+	return describe("when an anonymous client updates its cursor location", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
+						privilegeLevel: "owner",
+						project: { name: "Test Project"	},
 						publicAccess: "readAndWrite"
-					}, (error, {@user_id, @project_id}) => cb()
+					}, (error, {user_id, project_id}) => { this.user_id = user_id; this.project_id = project_id; return cb(); });
+				},
 				
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-						cb(e)
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
 				
-				(cb) =>
-					@clientA = RealTimeClient.connect()
-					@clientA.on "connectionAccepted", cb
+				cb => {
+					this.clientA = RealTimeClient.connect();
+					return this.clientA.on("connectionAccepted", cb);
+				},
 
-				(cb) =>
-					@clientA.emit "joinProject", {
-						project_id: @project_id
-					}, cb
+				cb => {
+					return this.clientA.emit("joinProject", {
+						project_id: this.project_id
+					}, cb);
+				},
 			
-				(cb) =>
-					RealTimeClient.setSession({}, cb)
+				cb => {
+					return RealTimeClient.setSession({}, cb);
+				},
 					
-				(cb) =>
-					@anonymous = RealTimeClient.connect()
-					@anonymous.on "connectionAccepted", cb	
+				cb => {
+					this.anonymous = RealTimeClient.connect();
+					return this.anonymous.on("connectionAccepted", cb);
+				},	
 					
-				(cb) =>
-					@anonymous.emit "joinProject", {
-						project_id: @project_id
-					}, cb
+				cb => {
+					return this.anonymous.emit("joinProject", {
+						project_id: this.project_id
+					}, cb);
+				},
 				
-				(cb) =>
-					@anonymous.emit "joinDoc", @doc_id, cb
+				cb => {
+					return this.anonymous.emit("joinDoc", this.doc_id, cb);
+				},
 					
-				(cb) =>
-					@updates = []
-					@clientA.on "clientTracking.clientUpdated", (data) =>
-						@updates.push data
+				cb => {
+					this.updates = [];
+					this.clientA.on("clientTracking.clientUpdated", data => {
+						return this.updates.push(data);
+					});
 
-					@anonymous.emit "clientTracking.updatePosition", {
-						row: @row = 42
-						column: @column = 36
-						doc_id: @doc_id
-					}, (error) ->
-						throw error if error?
-						setTimeout cb, 300 # Give the message a chance to reach client B.
-			], done
+					return this.anonymous.emit("clientTracking.updatePosition", {
+						row: (this.row = 42),
+						column: (this.column = 36),
+						doc_id: this.doc_id
+					}, function(error) {
+						if (error != null) { throw error; }
+						return setTimeout(cb, 300);
+					});
+				} // Give the message a chance to reach client B.
+			], done);
+		});
 			
-		it "should tell other clients about the update", ->
-			@updates.should.deep.equal [
+		return it("should tell other clients about the update", function() {
+			return this.updates.should.deep.equal([
 				{
-					row: @row
-					column: @column
-					doc_id: @doc_id
-					id: @anonymous.publicId
-					user_id: "anonymous-user"
+					row: this.row,
+					column: this.column,
+					doc_id: this.doc_id,
+					id: this.anonymous.publicId,
+					user_id: "anonymous-user",
 					name: ""
 				}
-			]
+			]);
+	});
+});
+});

+ 93 - 74
services/real-time/test/acceptance/coffee/DrainManagerTests.js

@@ -1,81 +1,100 @@
-RealTimeClient = require "./helpers/RealTimeClient"
-FixturesManager = require "./helpers/FixturesManager"
-
-expect = require("chai").expect
-
-async = require "async"
-request = require "request"
-
-Settings = require "settings-sharelatex"
-
-drain = (rate, callback) ->
-	request.post {
-		url: "http://localhost:3026/drain?rate=#{rate}"
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const RealTimeClient = require("./helpers/RealTimeClient");
+const FixturesManager = require("./helpers/FixturesManager");
+
+const {
+    expect
+} = require("chai");
+
+const async = require("async");
+const request = require("request");
+
+const Settings = require("settings-sharelatex");
+
+const drain = function(rate, callback) {
+	request.post({
+		url: `http://localhost:3026/drain?rate=${rate}`,
 		auth: {
 			user: Settings.internal.realTime.user,
 			pass: Settings.internal.realTime.pass
 		}
-	}, (error, response, data) ->
-		callback error, data
-	return null
-
-describe "DrainManagerTests", ->
-	before (done) ->
-		FixturesManager.setUpProject {
-			privilegeLevel: "owner"
+	}, (error, response, data) => callback(error, data));
+	return null;
+};
+
+describe("DrainManagerTests", function() {
+	before(function(done) {
+		FixturesManager.setUpProject({
+			privilegeLevel: "owner",
 			project: {
 				name: "Test Project"
 			}
-		}, (e, {@project_id, @user_id}) => done()
-		return null
-
-	before (done) ->
-		# cleanup to speedup reconnecting
-		@timeout(10000)
-		RealTimeClient.disconnectAllClients done
-
-	# trigger and check cleanup
-	it "should have disconnected all previous clients", (done) ->
-		RealTimeClient.getConnectedClients (error, data) ->
-			return done(error) if error
-			expect(data.length).to.equal(0)
-			done()
-
-	describe "with two clients in the project", ->
-		beforeEach (done) ->
-			async.series [
-				(cb) =>
-					@clientA = RealTimeClient.connect()
-					@clientA.on "connectionAccepted", cb
-
-				(cb) =>
-					@clientB = RealTimeClient.connect()
-					@clientB.on "connectionAccepted", cb
-
-				(cb) =>
-					@clientA.emit "joinProject", project_id: @project_id, cb
-
-				(cb) =>
-					@clientB.emit "joinProject", project_id: @project_id, cb
-			], done
-
-		describe "starting to drain", () ->
-			beforeEach (done) ->
-				async.parallel [
-					(cb) =>
-						@clientA.on "reconnectGracefully", cb
-					(cb) =>
-						@clientB.on "reconnectGracefully", cb
-
-					(cb) -> drain(2, cb)
-				], done
-
-			afterEach (done) ->
-				drain(0, done) # reset drain
-
-			it "should not timeout", ->
-				expect(true).to.equal(true)
-
-			it "should not have disconnected", ->
-				expect(@clientA.socket.connected).to.equal true
-				expect(@clientB.socket.connected).to.equal true
+		}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return done(); });
+		return null;
+	});
+
+	before(function(done) {
+		// cleanup to speedup reconnecting
+		this.timeout(10000);
+		return RealTimeClient.disconnectAllClients(done);
+	});
+
+	// trigger and check cleanup
+	it("should have disconnected all previous clients", done => RealTimeClient.getConnectedClients(function(error, data) {
+        if (error) { return done(error); }
+        expect(data.length).to.equal(0);
+        return done();
+    }));
+
+	return describe("with two clients in the project", function() {
+		beforeEach(function(done) {
+			return async.series([
+				cb => {
+					this.clientA = RealTimeClient.connect();
+					return this.clientA.on("connectionAccepted", cb);
+				},
+
+				cb => {
+					this.clientB = RealTimeClient.connect();
+					return this.clientB.on("connectionAccepted", cb);
+				},
+
+				cb => {
+					return this.clientA.emit("joinProject", {project_id: this.project_id}, cb);
+				},
+
+				cb => {
+					return this.clientB.emit("joinProject", {project_id: this.project_id}, cb);
+				}
+			], done);
+		});
+
+		return describe("starting to drain", function() {
+			beforeEach(function(done) {
+				return async.parallel([
+					cb => {
+						return this.clientA.on("reconnectGracefully", cb);
+					},
+					cb => {
+						return this.clientB.on("reconnectGracefully", cb);
+					},
+
+					cb => drain(2, cb)
+				], done);
+			});
+
+			afterEach(done => drain(0, done)); // reset drain
+
+			it("should not timeout", () => expect(true).to.equal(true));
+
+			return it("should not have disconnected", function() {
+				expect(this.clientA.socket.connected).to.equal(true);
+				return expect(this.clientB.socket.connected).to.equal(true);
+			});
+		});
+	});
+});

+ 200 - 151
services/real-time/test/acceptance/coffee/EarlyDisconnect.js

@@ -1,160 +1,209 @@
-async = require "async"
-{expect} = require("chai")
-
-RealTimeClient = require "./helpers/RealTimeClient"
-MockDocUpdaterServer = require "./helpers/MockDocUpdaterServer"
-MockWebServer = require "./helpers/MockWebServer"
-FixturesManager = require "./helpers/FixturesManager"
-
-settings = require "settings-sharelatex"
-redis = require "redis-sharelatex"
-rclient = redis.createClient(settings.redis.pubsub)
-rclientRT = redis.createClient(settings.redis.realtime)
-KeysRT = settings.redis.realtime.key_schema
-
-describe "EarlyDisconnect", ->
-	before (done) ->
-		MockDocUpdaterServer.run done
-
-	describe "when the client disconnects before joinProject completes", ->
-		before () ->
-			# slow down web-api requests to force the race condition
-			@actualWebAPIjoinProject = joinProject = MockWebServer.joinProject
-			MockWebServer.joinProject = (project_id, user_id, cb) ->
-				setTimeout () ->
-					joinProject(project_id, user_id, cb)
-				, 300
-
-		after () ->
-			MockWebServer.joinProject = @actualWebAPIjoinProject
-
-		beforeEach (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
-						privilegeLevel: "owner"
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const async = require("async");
+const {expect} = require("chai");
+
+const RealTimeClient = require("./helpers/RealTimeClient");
+const MockDocUpdaterServer = require("./helpers/MockDocUpdaterServer");
+const MockWebServer = require("./helpers/MockWebServer");
+const FixturesManager = require("./helpers/FixturesManager");
+
+const settings = require("settings-sharelatex");
+const redis = require("redis-sharelatex");
+const rclient = redis.createClient(settings.redis.pubsub);
+const rclientRT = redis.createClient(settings.redis.realtime);
+const KeysRT = settings.redis.realtime.key_schema;
+
+describe("EarlyDisconnect", function() {
+	before(done => MockDocUpdaterServer.run(done));
+
+	describe("when the client disconnects before joinProject completes", function() {
+		before(function() {
+			// slow down web-api requests to force the race condition
+			let joinProject;
+			this.actualWebAPIjoinProject = (joinProject = MockWebServer.joinProject);
+			return MockWebServer.joinProject = (project_id, user_id, cb) => setTimeout(() => joinProject(project_id, user_id, cb)
+            , 300);
+		});
+
+		after(function() {
+			return MockWebServer.joinProject = this.actualWebAPIjoinProject;
+		});
+
+		beforeEach(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
+						privilegeLevel: "owner",
 						project: {
 							name: "Test Project"
 						}
-					}, (e, {@project_id, @user_id}) => cb()
-
-				(cb) =>
-					@clientA = RealTimeClient.connect()
-					@clientA.on "connectionAccepted", cb
-
-				(cb) =>
-					@clientA.emit "joinProject", project_id: @project_id, (() ->)
-					# disconnect before joinProject completes
-					@clientA.on "disconnect", () -> cb()
-					@clientA.disconnect()
-
-				(cb) =>
-					# wait for joinDoc and subscribe
-					setTimeout cb, 500
-			], done
-
-		# we can force the race condition, there is no need to repeat too often
-		for attempt in Array.from(length: 5).map((_, i) -> i+1)
-			it "should not subscribe to the pub/sub channel anymore (race #{attempt})", (done) ->
-				rclient.pubsub 'CHANNELS', (err, resp) =>
-					return done(err) if err
-					expect(resp).to.not.include "editor-events:#{@project_id}"
-					done()
-				return null
-
-	describe "when the client disconnects before joinDoc completes", ->
-		beforeEach (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
-						privilegeLevel: "owner"
+					}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
+				},
+
+				cb => {
+					this.clientA = RealTimeClient.connect();
+					return this.clientA.on("connectionAccepted", cb);
+				},
+
+				cb => {
+					this.clientA.emit("joinProject", {project_id: this.project_id}, (function() {}));
+					// disconnect before joinProject completes
+					this.clientA.on("disconnect", () => cb());
+					return this.clientA.disconnect();
+				},
+
+				cb => {
+					// wait for joinDoc and subscribe
+					return setTimeout(cb, 500);
+				}
+			], done);
+		});
+
+		// we can force the race condition, there is no need to repeat too often
+		return Array.from(Array.from({length: 5}).map((_, i) => i+1)).map((attempt) =>
+			it(`should not subscribe to the pub/sub channel anymore (race ${attempt})`, function(done) {
+				rclient.pubsub('CHANNELS', (err, resp) => {
+					if (err) { return done(err); }
+					expect(resp).to.not.include(`editor-events:${this.project_id}`);
+					return done();
+				});
+				return null;
+			}));
+	});
+
+	describe("when the client disconnects before joinDoc completes", function() {
+		beforeEach(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
+						privilegeLevel: "owner",
 						project: {
 							name: "Test Project"
 						}
-					}, (e, {@project_id, @user_id}) => cb()
-
-				(cb) =>
-					@clientA = RealTimeClient.connect()
-					@clientA.on "connectionAccepted", cb
-
-				(cb) =>
-					@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
-						cb(error)
-
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-						cb(e)
-
-				(cb) =>
-					@clientA.emit "joinDoc", @doc_id, (() ->)
-					# disconnect before joinDoc completes
-					@clientA.on "disconnect", () -> cb()
-					@clientA.disconnect()
-
-				(cb) =>
-					# wait for subscribe and unsubscribe
-					setTimeout cb, 100
-			], done
-
-		# we can not force the race condition, so we have to try many times
-		for attempt in Array.from(length: 20).map((_, i) -> i+1)
-			it "should not subscribe to the pub/sub channels anymore (race #{attempt})", (done) ->
-				rclient.pubsub 'CHANNELS', (err, resp) =>
-					return done(err) if err
-					expect(resp).to.not.include "editor-events:#{@project_id}"
-
-					rclient.pubsub 'CHANNELS', (err, resp) =>
-						return done(err) if err
-						expect(resp).to.not.include "applied-ops:#{@doc_id}"
-						done()
-				return null
-
-	describe "when the client disconnects before clientTracking.updatePosition starts", ->
-		beforeEach (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
-						privilegeLevel: "owner"
+					}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
+				},
+
+				cb => {
+					this.clientA = RealTimeClient.connect();
+					return this.clientA.on("connectionAccepted", cb);
+				},
+
+				cb => {
+					return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
+						this.project = project;
+						this.privilegeLevel = privilegeLevel;
+						this.protocolVersion = protocolVersion;
+						return cb(error);
+					});
+				},
+
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
+
+				cb => {
+					this.clientA.emit("joinDoc", this.doc_id, (function() {}));
+					// disconnect before joinDoc completes
+					this.clientA.on("disconnect", () => cb());
+					return this.clientA.disconnect();
+				},
+
+				cb => {
+					// wait for subscribe and unsubscribe
+					return setTimeout(cb, 100);
+				}
+			], done);
+		});
+
+		// we can not force the race condition, so we have to try many times
+		return Array.from(Array.from({length: 20}).map((_, i) => i+1)).map((attempt) =>
+			it(`should not subscribe to the pub/sub channels anymore (race ${attempt})`, function(done) {
+				rclient.pubsub('CHANNELS', (err, resp) => {
+					if (err) { return done(err); }
+					expect(resp).to.not.include(`editor-events:${this.project_id}`);
+
+					return rclient.pubsub('CHANNELS', (err, resp) => {
+						if (err) { return done(err); }
+						expect(resp).to.not.include(`applied-ops:${this.doc_id}`);
+						return done();
+					});
+				});
+				return null;
+			}));
+	});
+
+	return describe("when the client disconnects before clientTracking.updatePosition starts", function() {
+		beforeEach(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
+						privilegeLevel: "owner",
 						project: {
 							name: "Test Project"
 						}
-					}, (e, {@project_id, @user_id}) => cb()
-
-				(cb) =>
-					@clientA = RealTimeClient.connect()
-					@clientA.on "connectionAccepted", cb
-
-				(cb) =>
-					@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
-						cb(error)
-
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-						cb(e)
-
-				(cb) =>
-					@clientA.emit "joinDoc", @doc_id, cb
-
-				(cb) =>
-					@clientA.emit "clientTracking.updatePosition", {
-							row: 42
-							column: 36
-							doc_id: @doc_id
-						}, (() ->)
-					# disconnect before updateClientPosition completes
-					@clientA.on "disconnect", () -> cb()
-					@clientA.disconnect()
-
-				(cb) =>
-					# wait for updateClientPosition
-					setTimeout cb, 100
-			], done
-
-		# we can not force the race condition, so we have to try many times
-		for attempt in Array.from(length: 20).map((_, i) -> i+1)
-			it "should not show the client as connected (race #{attempt})", (done) ->
-				rclientRT.smembers KeysRT.clientsInProject({project_id: @project_id}), (err, results) ->
-					return done(err) if err
-					expect(results).to.deep.equal([])
-					done()
-				return null
+					}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
+				},
+
+				cb => {
+					this.clientA = RealTimeClient.connect();
+					return this.clientA.on("connectionAccepted", cb);
+				},
+
+				cb => {
+					return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
+						this.project = project;
+						this.privilegeLevel = privilegeLevel;
+						this.protocolVersion = protocolVersion;
+						return cb(error);
+					});
+				},
+
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
+
+				cb => {
+					return this.clientA.emit("joinDoc", this.doc_id, cb);
+				},
+
+				cb => {
+					this.clientA.emit("clientTracking.updatePosition", {
+							row: 42,
+							column: 36,
+							doc_id: this.doc_id
+						}, (function() {}));
+					// disconnect before updateClientPosition completes
+					this.clientA.on("disconnect", () => cb());
+					return this.clientA.disconnect();
+				},
+
+				cb => {
+					// wait for updateClientPosition
+					return setTimeout(cb, 100);
+				}
+			], done);
+		});
+
+		// we can not force the race condition, so we have to try many times
+		return Array.from(Array.from({length: 20}).map((_, i) => i+1)).map((attempt) =>
+			it(`should not show the client as connected (race ${attempt})`, function(done) {
+				rclientRT.smembers(KeysRT.clientsInProject({project_id: this.project_id}), function(err, results) {
+					if (err) { return done(err); }
+					expect(results).to.deep.equal([]);
+					return done();
+				});
+				return null;
+			}));
+	});
+});

+ 75 - 52
services/real-time/test/acceptance/coffee/HttpControllerTests.js

@@ -1,68 +1,91 @@
-async = require('async')
-expect = require('chai').expect
-request = require('request').defaults({
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const async = require('async');
+const {
+    expect
+} = require('chai');
+const request = require('request').defaults({
 	baseUrl: 'http://localhost:3026'
-})
+});
 
-RealTimeClient = require "./helpers/RealTimeClient"
-FixturesManager = require "./helpers/FixturesManager"
+const RealTimeClient = require("./helpers/RealTimeClient");
+const FixturesManager = require("./helpers/FixturesManager");
 
-describe 'HttpControllerTests', ->
-	describe 'without a user', ->
-		it 'should return 404 for the client view', (done) ->
-			client_id = 'not-existing'
-			request.get {
-				url: "/clients/#{client_id}"
-				json: true
-			}, (error, response, data) ->
-				return done(error) if error
-				expect(response.statusCode).to.equal(404)
-				done()
+describe('HttpControllerTests', function() {
+	describe('without a user', () => it('should return 404 for the client view', function(done) {
+        const client_id = 'not-existing';
+        return request.get({
+            url: `/clients/${client_id}`,
+            json: true
+        }, function(error, response, data) {
+            if (error) { return done(error); }
+            expect(response.statusCode).to.equal(404);
+            return done();
+        });
+    }));
 
-	describe 'with a user and after joining a project', ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
+	return describe('with a user and after joining a project', function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
 						privilegeLevel: "owner"
-					}, (error, {@project_id, @user_id}) =>
-						cb(error)
+					}, (error, {project_id, user_id}) => {
+						this.project_id = project_id;
+						this.user_id = user_id;
+						return cb(error);
+					});
+				},
 
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {}, (error, {@doc_id}) =>
-						cb(error)
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {}, (error, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(error);
+					});
+				},
 
-				(cb) =>
-					@client = RealTimeClient.connect()
-					@client.on "connectionAccepted", cb
+				cb => {
+					this.client = RealTimeClient.connect();
+					return this.client.on("connectionAccepted", cb);
+				},
 
-				(cb) =>
-					@client.emit "joinProject", {@project_id}, cb
+				cb => {
+					return this.client.emit("joinProject", {project_id: this.project_id}, cb);
+				},
 
-				(cb) =>
-					@client.emit "joinDoc", @doc_id, cb
-			], done
+				cb => {
+					return this.client.emit("joinDoc", this.doc_id, cb);
+				}
+			], done);
+		});
 
-		it 'should send a client view', (done) ->
-			request.get {
-				url: "/clients/#{@client.socket.sessionid}"
+		return it('should send a client view', function(done) {
+			return request.get({
+				url: `/clients/${this.client.socket.sessionid}`,
 				json: true
-			}, (error, response, data) =>
-				return done(error) if error
-				expect(response.statusCode).to.equal(200)
-				expect(data.connected_time).to.exist
-				delete data.connected_time
-				# .email is not set in the session
-				delete data.email
+			}, (error, response, data) => {
+				if (error) { return done(error); }
+				expect(response.statusCode).to.equal(200);
+				expect(data.connected_time).to.exist;
+				delete data.connected_time;
+				// .email is not set in the session
+				delete data.email;
 				expect(data).to.deep.equal({
-					client_id: @client.socket.sessionid,
+					client_id: this.client.socket.sessionid,
 					first_name: 'Joe',
 					last_name: 'Bloggs',
-					project_id: @project_id,
-					user_id: @user_id,
+					project_id: this.project_id,
+					user_id: this.user_id,
 					rooms: [
-						@project_id,
-						@doc_id,
+						this.project_id,
+						this.doc_id,
 					]
-				})
-				done()
+				});
+				return done();
+			});
+		});
+	});
+});

+ 310 - 205
services/real-time/test/acceptance/coffee/JoinDocTests.js

@@ -1,246 +1,351 @@
-chai = require("chai")
-expect = chai.expect
-chai.should()
-
-RealTimeClient = require "./helpers/RealTimeClient"
-MockDocUpdaterServer = require "./helpers/MockDocUpdaterServer"
-FixturesManager = require "./helpers/FixturesManager"
-
-async = require "async"
-
-describe "joinDoc", ->
-	before ->
-		@lines = ["test", "doc", "lines"]
-		@version = 42
-		@ops = ["mock", "doc", "ops"]
-		@ranges = {"mock": "ranges"}
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const chai = require("chai");
+const {
+    expect
+} = chai;
+chai.should();
+
+const RealTimeClient = require("./helpers/RealTimeClient");
+const MockDocUpdaterServer = require("./helpers/MockDocUpdaterServer");
+const FixturesManager = require("./helpers/FixturesManager");
+
+const async = require("async");
+
+describe("joinDoc", function() {
+	before(function() {
+		this.lines = ["test", "doc", "lines"];
+		this.version = 42;
+		this.ops = ["mock", "doc", "ops"];
+		return this.ranges = {"mock": "ranges"};});
 			
-	describe "when authorised readAndWrite", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
+	describe("when authorised readAndWrite", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
 						privilegeLevel: "readAndWrite"
-					}, (e, {@project_id, @user_id}) =>
-						cb(e)
+					}, (e, {project_id, user_id}) => {
+						this.project_id = project_id;
+						this.user_id = user_id;
+						return cb(e);
+					});
+				},
 					
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops, @ranges}, (e, {@doc_id}) =>
-						cb(e)
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops, ranges: this.ranges}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
 
-				(cb) =>
-					@client = RealTimeClient.connect()
-					@client.on "connectionAccepted", cb
+				cb => {
+					this.client = RealTimeClient.connect();
+					return this.client.on("connectionAccepted", cb);
+				},
 						
-				(cb) =>
-					@client.emit "joinProject", project_id: @project_id, cb
+				cb => {
+					return this.client.emit("joinProject", {project_id: this.project_id}, cb);
+				},
 				
-				(cb) =>
-					@client.emit "joinDoc", @doc_id, (error, @returnedArgs...) => cb(error)
-			], done
-
-		it "should get the doc from the doc updater", ->
-			MockDocUpdaterServer.getDocument
-				.calledWith(@project_id, @doc_id, -1)
-				.should.equal true
+				cb => {
+					return this.client.emit("joinDoc", this.doc_id, (error, ...rest) => { [...this.returnedArgs] = Array.from(rest); return cb(error); });
+				}
+			], done);
+		});
+
+		it("should get the doc from the doc updater", function() {
+			return MockDocUpdaterServer.getDocument
+				.calledWith(this.project_id, this.doc_id, -1)
+				.should.equal(true);
+		});
 		
-		it "should return the doc lines, version, ranges and ops", ->
-			@returnedArgs.should.deep.equal [@lines, @version, @ops, @ranges]
+		it("should return the doc lines, version, ranges and ops", function() {
+			return this.returnedArgs.should.deep.equal([this.lines, this.version, this.ops, this.ranges]);
+	});
 			
-		it "should have joined the doc room", (done) ->
-			RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
-				expect(@doc_id in client.rooms).to.equal true
-				done()
+		return it("should have joined the doc room", function(done) {
+			return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
+				expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(true);
+				return done();
+			});
+		});
+	});
 		
-	describe "when authorised readOnly", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
+	describe("when authorised readOnly", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
 						privilegeLevel: "readOnly"
-					}, (e, {@project_id, @user_id}) =>
-						cb(e)
+					}, (e, {project_id, user_id}) => {
+						this.project_id = project_id;
+						this.user_id = user_id;
+						return cb(e);
+					});
+				},
 					
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops, @ranges}, (e, {@doc_id}) =>
-						cb(e)
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops, ranges: this.ranges}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
 
-				(cb) =>
-					@client = RealTimeClient.connect()
-					@client.on "connectionAccepted", cb
+				cb => {
+					this.client = RealTimeClient.connect();
+					return this.client.on("connectionAccepted", cb);
+				},
 						
-				(cb) =>
-					@client.emit "joinProject", project_id: @project_id, cb
+				cb => {
+					return this.client.emit("joinProject", {project_id: this.project_id}, cb);
+				},
 				
-				(cb) =>
-					@client.emit "joinDoc", @doc_id, (error, @returnedArgs...) => cb(error)
-			], done
-
-		it "should get the doc from the doc updater", ->
-			MockDocUpdaterServer.getDocument
-				.calledWith(@project_id, @doc_id, -1)
-				.should.equal true
+				cb => {
+					return this.client.emit("joinDoc", this.doc_id, (error, ...rest) => { [...this.returnedArgs] = Array.from(rest); return cb(error); });
+				}
+			], done);
+		});
+
+		it("should get the doc from the doc updater", function() {
+			return MockDocUpdaterServer.getDocument
+				.calledWith(this.project_id, this.doc_id, -1)
+				.should.equal(true);
+		});
 		
-		it "should return the doc lines, version, ranges and ops", ->
-			@returnedArgs.should.deep.equal [@lines, @version, @ops, @ranges]
+		it("should return the doc lines, version, ranges and ops", function() {
+			return this.returnedArgs.should.deep.equal([this.lines, this.version, this.ops, this.ranges]);
+	});
 			
-		it "should have joined the doc room", (done) ->
-			RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
-				expect(@doc_id in client.rooms).to.equal true
-				done()
+		return it("should have joined the doc room", function(done) {
+			return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
+				expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(true);
+				return done();
+			});
+		});
+	});
 		
-	describe "when authorised as owner", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
+	describe("when authorised as owner", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
 						privilegeLevel: "owner"
-					}, (e, {@project_id, @user_id}) =>
-						cb(e)
+					}, (e, {project_id, user_id}) => {
+						this.project_id = project_id;
+						this.user_id = user_id;
+						return cb(e);
+					});
+				},
 					
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops, @ranges}, (e, {@doc_id}) =>
-						cb(e)
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops, ranges: this.ranges}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
 
-				(cb) =>
-					@client = RealTimeClient.connect()
-					@client.on "connectionAccepted", cb
+				cb => {
+					this.client = RealTimeClient.connect();
+					return this.client.on("connectionAccepted", cb);
+				},
 						
-				(cb) =>
-					@client.emit "joinProject", project_id: @project_id, cb
+				cb => {
+					return this.client.emit("joinProject", {project_id: this.project_id}, cb);
+				},
 				
-				(cb) =>
-					@client.emit "joinDoc", @doc_id, (error, @returnedArgs...) => cb(error)
-			], done
-
-		it "should get the doc from the doc updater", ->
-			MockDocUpdaterServer.getDocument
-				.calledWith(@project_id, @doc_id, -1)
-				.should.equal true
+				cb => {
+					return this.client.emit("joinDoc", this.doc_id, (error, ...rest) => { [...this.returnedArgs] = Array.from(rest); return cb(error); });
+				}
+			], done);
+		});
+
+		it("should get the doc from the doc updater", function() {
+			return MockDocUpdaterServer.getDocument
+				.calledWith(this.project_id, this.doc_id, -1)
+				.should.equal(true);
+		});
 		
-		it "should return the doc lines, version, ranges and ops", ->
-			@returnedArgs.should.deep.equal [@lines, @version, @ops, @ranges]
+		it("should return the doc lines, version, ranges and ops", function() {
+			return this.returnedArgs.should.deep.equal([this.lines, this.version, this.ops, this.ranges]);
+	});
 			
-		it "should have joined the doc room", (done) ->
-			RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
-				expect(@doc_id in client.rooms).to.equal true
-				done()
-
-	# It is impossible to write an acceptance test to test joining an unauthorized
-	# project, since joinProject already catches that. If you can join a project,
-	# then you can join a doc in that project.
+		return it("should have joined the doc room", function(done) {
+			return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
+				expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(true);
+				return done();
+			});
+		});
+	});
+
+	// It is impossible to write an acceptance test to test joining an unauthorized
+	// project, since joinProject already catches that. If you can join a project,
+	// then you can join a doc in that project.
 			
-	describe "with a fromVersion", ->
-		before (done) ->
-			@fromVersion = 36
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
+	describe("with a fromVersion", function() {
+		before(function(done) {
+			this.fromVersion = 36;
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
 						privilegeLevel: "readAndWrite"
-					}, (e, {@project_id, @user_id}) =>
-						cb(e)
+					}, (e, {project_id, user_id}) => {
+						this.project_id = project_id;
+						this.user_id = user_id;
+						return cb(e);
+					});
+				},
 					
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops, @ranges}, (e, {@doc_id}) =>
-						cb(e)
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops, ranges: this.ranges}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
 
-				(cb) =>
-					@client = RealTimeClient.connect()
-					@client.on "connectionAccepted", cb
+				cb => {
+					this.client = RealTimeClient.connect();
+					return this.client.on("connectionAccepted", cb);
+				},
 						
-				(cb) =>
-					@client.emit "joinProject", project_id: @project_id, cb
+				cb => {
+					return this.client.emit("joinProject", {project_id: this.project_id}, cb);
+				},
 				
-				(cb) =>
-					@client.emit "joinDoc", @doc_id, @fromVersion, (error, @returnedArgs...) => cb(error)
-			], done
-
-		it "should get the doc from the doc updater with the fromVersion", ->
-			MockDocUpdaterServer.getDocument
-				.calledWith(@project_id, @doc_id, @fromVersion)
-				.should.equal true
+				cb => {
+					return this.client.emit("joinDoc", this.doc_id, this.fromVersion, (error, ...rest) => { [...this.returnedArgs] = Array.from(rest); return cb(error); });
+				}
+			], done);
+		});
+
+		it("should get the doc from the doc updater with the fromVersion", function() {
+			return MockDocUpdaterServer.getDocument
+				.calledWith(this.project_id, this.doc_id, this.fromVersion)
+				.should.equal(true);
+		});
 		
-		it "should return the doc lines, version, ranges and ops", ->
-			@returnedArgs.should.deep.equal [@lines, @version, @ops, @ranges]
+		it("should return the doc lines, version, ranges and ops", function() {
+			return this.returnedArgs.should.deep.equal([this.lines, this.version, this.ops, this.ranges]);
+	});
 			
-		it "should have joined the doc room", (done) ->
-			RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
-				expect(@doc_id in client.rooms).to.equal true
-				done()
-
-	describe "with options", ->
-		before (done) ->
-			@options = { encodeRanges: true }
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
+		return it("should have joined the doc room", function(done) {
+			return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
+				expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(true);
+				return done();
+			});
+		});
+	});
+
+	describe("with options", function() {
+		before(function(done) {
+			this.options = { encodeRanges: true };
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
 						privilegeLevel: "readAndWrite"
-					}, (e, {@project_id, @user_id}) =>
-						cb(e)
-
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops, @ranges}, (e, {@doc_id}) =>
-						cb(e)
-
-				(cb) =>
-					@client = RealTimeClient.connect()
-					@client.on "connectionAccepted", cb
-
-				(cb) =>
-					@client.emit "joinProject", project_id: @project_id, cb
-
-				(cb) =>
-					@client.emit "joinDoc", @doc_id, @options, (error, @returnedArgs...) => cb(error)
-			], done
-
-		it "should get the doc from the doc updater with the default fromVersion", ->
-			MockDocUpdaterServer.getDocument
-				.calledWith(@project_id, @doc_id, -1)
-				.should.equal true
-
-		it "should return the doc lines, version, ranges and ops", ->
-			@returnedArgs.should.deep.equal [@lines, @version, @ops, @ranges]
-
-		it "should have joined the doc room", (done) ->
-			RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
-				expect(@doc_id in client.rooms).to.equal true
-				done()
-
-	describe "with fromVersion and options", ->
-		before (done) ->
-			@fromVersion = 36
-			@options = { encodeRanges: true }
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
+					}, (e, {project_id, user_id}) => {
+						this.project_id = project_id;
+						this.user_id = user_id;
+						return cb(e);
+					});
+				},
+
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops, ranges: this.ranges}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
+
+				cb => {
+					this.client = RealTimeClient.connect();
+					return this.client.on("connectionAccepted", cb);
+				},
+
+				cb => {
+					return this.client.emit("joinProject", {project_id: this.project_id}, cb);
+				},
+
+				cb => {
+					return this.client.emit("joinDoc", this.doc_id, this.options, (error, ...rest) => { [...this.returnedArgs] = Array.from(rest); return cb(error); });
+				}
+			], done);
+		});
+
+		it("should get the doc from the doc updater with the default fromVersion", function() {
+			return MockDocUpdaterServer.getDocument
+				.calledWith(this.project_id, this.doc_id, -1)
+				.should.equal(true);
+		});
+
+		it("should return the doc lines, version, ranges and ops", function() {
+			return this.returnedArgs.should.deep.equal([this.lines, this.version, this.ops, this.ranges]);
+	});
+
+		return it("should have joined the doc room", function(done) {
+			return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
+				expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(true);
+				return done();
+			});
+		});
+	});
+
+	return describe("with fromVersion and options", function() {
+		before(function(done) {
+			this.fromVersion = 36;
+			this.options = { encodeRanges: true };
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
 						privilegeLevel: "readAndWrite"
-					}, (e, {@project_id, @user_id}) =>
-						cb(e)
+					}, (e, {project_id, user_id}) => {
+						this.project_id = project_id;
+						this.user_id = user_id;
+						return cb(e);
+					});
+				},
 
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops, @ranges}, (e, {@doc_id}) =>
-						cb(e)
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops, ranges: this.ranges}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
 
-				(cb) =>
-					@client = RealTimeClient.connect()
-					@client.on "connectionAccepted", cb
+				cb => {
+					this.client = RealTimeClient.connect();
+					return this.client.on("connectionAccepted", cb);
+				},
 
-				(cb) =>
-					@client.emit "joinProject", project_id: @project_id, cb
+				cb => {
+					return this.client.emit("joinProject", {project_id: this.project_id}, cb);
+				},
 
-				(cb) =>
-					@client.emit "joinDoc", @doc_id, @fromVersion, @options, (error, @returnedArgs...) => cb(error)
-			], done
+				cb => {
+					return this.client.emit("joinDoc", this.doc_id, this.fromVersion, this.options, (error, ...rest) => { [...this.returnedArgs] = Array.from(rest); return cb(error); });
+				}
+			], done);
+		});
 
-		it "should get the doc from the doc updater with the fromVersion", ->
-			MockDocUpdaterServer.getDocument
-				.calledWith(@project_id, @doc_id, @fromVersion)
-				.should.equal true
+		it("should get the doc from the doc updater with the fromVersion", function() {
+			return MockDocUpdaterServer.getDocument
+				.calledWith(this.project_id, this.doc_id, this.fromVersion)
+				.should.equal(true);
+		});
 
-		it "should return the doc lines, version, ranges and ops", ->
-			@returnedArgs.should.deep.equal [@lines, @version, @ops, @ranges]
+		it("should return the doc lines, version, ranges and ops", function() {
+			return this.returnedArgs.should.deep.equal([this.lines, this.version, this.ops, this.ranges]);
+	});
 
-		it "should have joined the doc room", (done) ->
-			RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
-				expect(@doc_id in client.rooms).to.equal true
-				done()
+		return it("should have joined the doc room", function(done) {
+			return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
+				expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(true);
+				return done();
+			});
+		});
+	});
+});

+ 135 - 81
services/real-time/test/acceptance/coffee/JoinProjectTests.js

@@ -1,108 +1,162 @@
-chai = require("chai")
-expect = chai.expect
-chai.should()
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const chai = require("chai");
+const {
+    expect
+} = chai;
+chai.should();
 
-RealTimeClient = require "./helpers/RealTimeClient"
-MockWebServer = require "./helpers/MockWebServer"
-FixturesManager = require "./helpers/FixturesManager"
+const RealTimeClient = require("./helpers/RealTimeClient");
+const MockWebServer = require("./helpers/MockWebServer");
+const FixturesManager = require("./helpers/FixturesManager");
 
-async = require "async"
+const async = require("async");
 
-describe "joinProject", ->
-	describe "when authorized", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
-						privilegeLevel: "owner"
+describe("joinProject", function() {
+	describe("when authorized", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
+						privilegeLevel: "owner",
 						project: {
 							name: "Test Project"
 						}
-					}, (e, {@project_id, @user_id}) =>
-						cb(e)
+					}, (e, {project_id, user_id}) => {
+						this.project_id = project_id;
+						this.user_id = user_id;
+						return cb(e);
+					});
+				},
 
-				(cb) =>
-					@client = RealTimeClient.connect()
-					@client.on "connectionAccepted", cb
+				cb => {
+					this.client = RealTimeClient.connect();
+					return this.client.on("connectionAccepted", cb);
+				},
 						
-				(cb) =>
-					@client.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
-						cb(error)
-			], done
+				cb => {
+					return this.client.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
+						this.project = project;
+						this.privilegeLevel = privilegeLevel;
+						this.protocolVersion = protocolVersion;
+						return cb(error);
+					});
+				}
+			], done);
+		});
 					
-		it "should get the project from web", ->
-			MockWebServer.joinProject
-				.calledWith(@project_id, @user_id)
-				.should.equal true
+		it("should get the project from web", function() {
+			return MockWebServer.joinProject
+				.calledWith(this.project_id, this.user_id)
+				.should.equal(true);
+		});
 					
-		it "should return the project", ->
-			@project.should.deep.equal {
+		it("should return the project", function() {
+			return this.project.should.deep.equal({
 				name: "Test Project"
-			}
+			});
+	});
 		
-		it "should return the privilege level", ->
-			@privilegeLevel.should.equal "owner"
+		it("should return the privilege level", function() {
+			return this.privilegeLevel.should.equal("owner");
+		});
 		
-		it "should return the protocolVersion", ->
-			@protocolVersion.should.equal 2
+		it("should return the protocolVersion", function() {
+			return this.protocolVersion.should.equal(2);
+		});
 			
-		it "should have joined the project room", (done) ->
-			RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
-				expect(@project_id in client.rooms).to.equal true
-				done()
+		it("should have joined the project room", function(done) {
+			return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
+				expect(Array.from(client.rooms).includes(this.project_id)).to.equal(true);
+				return done();
+			});
+		});
 				
-		it "should have marked the user as connected", (done) ->
-			@client.emit "clientTracking.getConnectedUsers", (error, users) =>
-				connected = false
-				for user in users
-					if user.client_id == @client.publicId and user.user_id == @user_id
-						connected = true
-						break
-				expect(connected).to.equal true
-				done()
+		return it("should have marked the user as connected", function(done) {
+			return this.client.emit("clientTracking.getConnectedUsers", (error, users) => {
+				let connected = false;
+				for (let user of Array.from(users)) {
+					if ((user.client_id === this.client.publicId) && (user.user_id === this.user_id)) {
+						connected = true;
+						break;
+					}
+				}
+				expect(connected).to.equal(true);
+				return done();
+			});
+		});
+	});
 			
-	describe "when not authorized", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
-						privilegeLevel: null
+	describe("when not authorized", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
+						privilegeLevel: null,
 						project: {
 							name: "Test Project"
 						}
-					}, (e, {@project_id, @user_id}) =>
-						cb(e)
+					}, (e, {project_id, user_id}) => {
+						this.project_id = project_id;
+						this.user_id = user_id;
+						return cb(e);
+					});
+				},
 
-				(cb) =>
-					@client = RealTimeClient.connect()
-					@client.on "connectionAccepted", cb
+				cb => {
+					this.client = RealTimeClient.connect();
+					return this.client.on("connectionAccepted", cb);
+				},
 						
-				(cb) =>
-					@client.emit "joinProject", project_id: @project_id, (@error, @project, @privilegeLevel, @protocolVersion) =>
-						cb()
-			], done
+				cb => {
+					return this.client.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
+						this.error = error;
+						this.project = project;
+						this.privilegeLevel = privilegeLevel;
+						this.protocolVersion = protocolVersion;
+						return cb();
+					});
+				}
+			], done);
+		});
 		
-		it "should return an error", ->
-			@error.message.should.equal "not authorized"
+		it("should return an error", function() {
+			return this.error.message.should.equal("not authorized");
+		});
 			
-		it "should not have joined the project room", (done) ->
-			RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
-				expect(@project_id in client.rooms).to.equal false
-				done()
+		return it("should not have joined the project room", function(done) {
+			return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
+				expect(Array.from(client.rooms).includes(this.project_id)).to.equal(false);
+				return done();
+			});
+		});
+	});
 
-	describe "when over rate limit", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					@client = RealTimeClient.connect()
-					@client.on "connectionAccepted", cb
+	return describe("when over rate limit", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					this.client = RealTimeClient.connect();
+					return this.client.on("connectionAccepted", cb);
+				},
 
-				(cb) =>
-					@client.emit "joinProject", project_id: 'rate-limited', (@error) =>
-						cb()
-			], done
+				cb => {
+					return this.client.emit("joinProject", {project_id: 'rate-limited'}, error => {
+						this.error = error;
+						return cb();
+					});
+				}
+			], done);
+		});
 
-		it "should return a TooManyRequests error code", ->
-			@error.message.should.equal "rate-limit hit when joining project"
-			@error.code.should.equal "TooManyRequests"
+		return it("should return a TooManyRequests error code", function() {
+			this.error.message.should.equal("rate-limit hit when joining project");
+			return this.error.code.should.equal("TooManyRequests");
+		});
+	});
+});
 

+ 104 - 69
services/real-time/test/acceptance/coffee/LeaveDocTests.js

@@ -1,86 +1,121 @@
-chai = require("chai")
-expect = chai.expect
-chai.should()
-sinon = require("sinon")
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const chai = require("chai");
+const {
+    expect
+} = chai;
+chai.should();
+const sinon = require("sinon");
 
-RealTimeClient = require "./helpers/RealTimeClient"
-MockDocUpdaterServer = require "./helpers/MockDocUpdaterServer"
-FixturesManager = require "./helpers/FixturesManager"
-logger = require("logger-sharelatex")
+const RealTimeClient = require("./helpers/RealTimeClient");
+const MockDocUpdaterServer = require("./helpers/MockDocUpdaterServer");
+const FixturesManager = require("./helpers/FixturesManager");
+const logger = require("logger-sharelatex");
 
-async = require "async"
+const async = require("async");
 
-describe "leaveDoc", ->
-	before ->
-		@lines = ["test", "doc", "lines"]
-		@version = 42
-		@ops = ["mock", "doc", "ops"]
-		sinon.spy(logger, "error")
-		sinon.spy(logger, "warn")
-		sinon.spy(logger, "log")
-		@other_doc_id = FixturesManager.getRandomId()
+describe("leaveDoc", function() {
+	before(function() {
+		this.lines = ["test", "doc", "lines"];
+		this.version = 42;
+		this.ops = ["mock", "doc", "ops"];
+		sinon.spy(logger, "error");
+		sinon.spy(logger, "warn");
+		sinon.spy(logger, "log");
+		return this.other_doc_id = FixturesManager.getRandomId();
+	});
 	
-	after ->
-		logger.error.restore() # remove the spy
-		logger.warn.restore()
-		logger.log.restore()
+	after(function() {
+		logger.error.restore(); // remove the spy
+		logger.warn.restore();
+		return logger.log.restore();
+	});
 
-	describe "when joined to a doc", ->
-		beforeEach (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
+	return describe("when joined to a doc", function() {
+		beforeEach(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
 						privilegeLevel: "readAndWrite"
-					}, (e, {@project_id, @user_id}) =>
-						cb(e)
+					}, (e, {project_id, user_id}) => {
+						this.project_id = project_id;
+						this.user_id = user_id;
+						return cb(e);
+					});
+				},
 					
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-						cb(e)
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
 						
-				(cb) =>
-					@client = RealTimeClient.connect()
-					@client.on "connectionAccepted", cb
+				cb => {
+					this.client = RealTimeClient.connect();
+					return this.client.on("connectionAccepted", cb);
+				},
 						
-				(cb) =>
-					@client.emit "joinProject", project_id: @project_id, cb
+				cb => {
+					return this.client.emit("joinProject", {project_id: this.project_id}, cb);
+				},
 				
-				(cb) =>
-					@client.emit "joinDoc", @doc_id, (error, @returnedArgs...) => cb(error)
-			], done
+				cb => {
+					return this.client.emit("joinDoc", this.doc_id, (error, ...rest) => { [...this.returnedArgs] = Array.from(rest); return cb(error); });
+				}
+			], done);
+		});
 							
-		describe "then leaving the doc", ->
-			beforeEach (done) ->
-				@client.emit "leaveDoc", @doc_id, (error) ->
-					throw error if error?
-					done()
+		describe("then leaving the doc", function() {
+			beforeEach(function(done) {
+				return this.client.emit("leaveDoc", this.doc_id, function(error) {
+					if (error != null) { throw error; }
+					return done();
+				});
+			});
 			
-			it "should have left the doc room", (done) ->
-				RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
-					expect(@doc_id in client.rooms).to.equal false
-					done()
+			return it("should have left the doc room", function(done) {
+				return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
+					expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(false);
+					return done();
+				});
+			});
+		});
 
-		describe "when sending a leaveDoc request before the previous joinDoc request has completed", ->
-			beforeEach (done) ->
-				@client.emit "leaveDoc", @doc_id, () ->
-				@client.emit "joinDoc", @doc_id, () ->
-				@client.emit "leaveDoc", @doc_id, (error) ->
-					throw error if error?
-					done()
+		describe("when sending a leaveDoc request before the previous joinDoc request has completed", function() {
+			beforeEach(function(done) {
+				this.client.emit("leaveDoc", this.doc_id, function() {});
+				this.client.emit("joinDoc", this.doc_id, function() {});
+				return this.client.emit("leaveDoc", this.doc_id, function(error) {
+					if (error != null) { throw error; }
+					return done();
+				});
+			});
 
-			it "should not trigger an error", ->
-				sinon.assert.neverCalledWith(logger.error, sinon.match.any, "not subscribed - shouldn't happen")
+			it("should not trigger an error", () => sinon.assert.neverCalledWith(logger.error, sinon.match.any, "not subscribed - shouldn't happen"));
 
-			it "should have left the doc room", (done) ->
-				RealTimeClient.getConnectedClient @client.socket.sessionid, (error, client) =>
-					expect(@doc_id in client.rooms).to.equal false
-					done()
+			return it("should have left the doc room", function(done) {
+				return RealTimeClient.getConnectedClient(this.client.socket.sessionid, (error, client) => {
+					expect(Array.from(client.rooms).includes(this.doc_id)).to.equal(false);
+					return done();
+				});
+			});
+		});
 
-		describe "when sending a leaveDoc for a room the client has not joined ", ->
-			beforeEach (done) ->
-				@client.emit "leaveDoc", @other_doc_id, (error) ->
-					throw error if error?
-					done()
+		return describe("when sending a leaveDoc for a room the client has not joined ", function() {
+			beforeEach(function(done) {
+				return this.client.emit("leaveDoc", this.other_doc_id, function(error) {
+					if (error != null) { throw error; }
+					return done();
+				});
+			});
 
-			it "should trigger a low level message only", ->
-				sinon.assert.calledWith(logger.log, sinon.match.any, "ignoring request from client to leave room it is not in")
+			return it("should trigger a low level message only", () => sinon.assert.calledWith(logger.log, sinon.match.any, "ignoring request from client to leave room it is not in"));
+		});
+	});
+});

+ 200 - 141
services/real-time/test/acceptance/coffee/LeaveProjectTests.js

@@ -1,147 +1,206 @@
-RealTimeClient = require "./helpers/RealTimeClient"
-MockDocUpdaterServer = require "./helpers/MockDocUpdaterServer"
-FixturesManager = require "./helpers/FixturesManager"
-
-async = require "async"
-
-settings = require "settings-sharelatex"
-redis = require "redis-sharelatex"
-rclient = redis.createClient(settings.redis.pubsub)
-
-describe "leaveProject", ->
-	before (done) ->
-		MockDocUpdaterServer.run done
-
-	describe "with other clients in the project", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
-						privilegeLevel: "owner"
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const RealTimeClient = require("./helpers/RealTimeClient");
+const MockDocUpdaterServer = require("./helpers/MockDocUpdaterServer");
+const FixturesManager = require("./helpers/FixturesManager");
+
+const async = require("async");
+
+const settings = require("settings-sharelatex");
+const redis = require("redis-sharelatex");
+const rclient = redis.createClient(settings.redis.pubsub);
+
+describe("leaveProject", function() {
+	before(done => MockDocUpdaterServer.run(done));
+
+	describe("with other clients in the project", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
+						privilegeLevel: "owner",
 						project: {
 							name: "Test Project"
 						}
-					}, (e, {@project_id, @user_id}) => cb()
-
-				(cb) =>
-					@clientA = RealTimeClient.connect()
-					@clientA.on "connectionAccepted", cb
-
-				(cb) =>
-					@clientB = RealTimeClient.connect()
-					@clientB.on "connectionAccepted", cb
-
-					@clientBDisconnectMessages = []
-					@clientB.on "clientTracking.clientDisconnected", (data) =>
-						@clientBDisconnectMessages.push data
-
-				(cb) =>
-					@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
-						cb(error)
-
-				(cb) =>
-					@clientB.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
-						cb(error)
-
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-						cb(e)
-
-				(cb) =>
-					@clientA.emit "joinDoc", @doc_id, cb
-				(cb) =>
-					@clientB.emit "joinDoc", @doc_id, cb
-
-				(cb) =>
-					# leaveProject is called when the client disconnects
-					@clientA.on "disconnect", () -> cb()
-					@clientA.disconnect()
-
-				(cb) =>
-					# The API waits a little while before flushing changes
-					setTimeout done, 1000
-
-			], done
-
-		it "should emit a disconnect message to the room", ->
-			@clientBDisconnectMessages.should.deep.equal [@clientA.publicId]
-
-		it "should no longer list the client in connected users", (done) ->
-			@clientB.emit "clientTracking.getConnectedUsers", (error, users) =>
-				for user in users
-					if user.client_id == @clientA.publicId
-						throw "Expected clientA to not be listed in connected users"
-				return done()
-
-		it "should not flush the project to the document updater", ->
-			MockDocUpdaterServer.deleteProject
-				.calledWith(@project_id)
-				.should.equal false
-
-		it "should remain subscribed to the editor-events channels", (done) ->
-			rclient.pubsub 'CHANNELS', (err, resp) =>
-				return done(err) if err
-				resp.should.include "editor-events:#{@project_id}"
-				done()
-			return null
-
-		it "should remain subscribed to the applied-ops channels", (done) ->
-			rclient.pubsub 'CHANNELS', (err, resp) =>
-				return done(err) if err
-				resp.should.include "applied-ops:#{@doc_id}"
-				done()
-			return null
-
-	describe "with no other clients in the project", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
-						privilegeLevel: "owner"
+					}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
+				},
+
+				cb => {
+					this.clientA = RealTimeClient.connect();
+					return this.clientA.on("connectionAccepted", cb);
+				},
+
+				cb => {
+					this.clientB = RealTimeClient.connect();
+					this.clientB.on("connectionAccepted", cb);
+
+					this.clientBDisconnectMessages = [];
+					return this.clientB.on("clientTracking.clientDisconnected", data => {
+						return this.clientBDisconnectMessages.push(data);
+					});
+				},
+
+				cb => {
+					return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
+						this.project = project;
+						this.privilegeLevel = privilegeLevel;
+						this.protocolVersion = protocolVersion;
+						return cb(error);
+					});
+				},
+
+				cb => {
+					return this.clientB.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
+						this.project = project;
+						this.privilegeLevel = privilegeLevel;
+						this.protocolVersion = protocolVersion;
+						return cb(error);
+					});
+				},
+
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
+
+				cb => {
+					return this.clientA.emit("joinDoc", this.doc_id, cb);
+				},
+				cb => {
+					return this.clientB.emit("joinDoc", this.doc_id, cb);
+				},
+
+				cb => {
+					// leaveProject is called when the client disconnects
+					this.clientA.on("disconnect", () => cb());
+					return this.clientA.disconnect();
+				},
+
+				cb => {
+					// The API waits a little while before flushing changes
+					return setTimeout(done, 1000);
+				}
+
+			], done);
+		});
+
+		it("should emit a disconnect message to the room", function() {
+			return this.clientBDisconnectMessages.should.deep.equal([this.clientA.publicId]);
+	});
+
+		it("should no longer list the client in connected users", function(done) {
+			return this.clientB.emit("clientTracking.getConnectedUsers", (error, users) => {
+				for (let user of Array.from(users)) {
+					if (user.client_id === this.clientA.publicId) {
+						throw "Expected clientA to not be listed in connected users";
+					}
+				}
+				return done();
+			});
+		});
+
+		it("should not flush the project to the document updater", function() {
+			return MockDocUpdaterServer.deleteProject
+				.calledWith(this.project_id)
+				.should.equal(false);
+		});
+
+		it("should remain subscribed to the editor-events channels", function(done) {
+			rclient.pubsub('CHANNELS', (err, resp) => {
+				if (err) { return done(err); }
+				resp.should.include(`editor-events:${this.project_id}`);
+				return done();
+			});
+			return null;
+		});
+
+		return it("should remain subscribed to the applied-ops channels", function(done) {
+			rclient.pubsub('CHANNELS', (err, resp) => {
+				if (err) { return done(err); }
+				resp.should.include(`applied-ops:${this.doc_id}`);
+				return done();
+			});
+			return null;
+		});
+	});
+
+	return describe("with no other clients in the project", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
+						privilegeLevel: "owner",
 						project: {
 							name: "Test Project"
 						}
-					}, (e, {@project_id, @user_id}) => cb()
-
-				(cb) =>
-					@clientA = RealTimeClient.connect()
-					@clientA.on "connect", cb
-
-				(cb) =>
-					@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
-						cb(error)
-
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-						cb(e)
-				(cb) =>
-					@clientA.emit "joinDoc", @doc_id, cb
-
-				(cb) =>
-					# leaveProject is called when the client disconnects
-					@clientA.on "disconnect", () -> cb()
-					@clientA.disconnect()
-
-				(cb) =>
-					# The API waits a little while before flushing changes
-					setTimeout done, 1000
-			], done
-
-		it "should flush the project to the document updater", ->
-			MockDocUpdaterServer.deleteProject
-				.calledWith(@project_id)
-				.should.equal true
-
-		it "should not subscribe to the editor-events channels anymore", (done) ->
-			rclient.pubsub 'CHANNELS', (err, resp) =>
-				return done(err) if err
-				resp.should.not.include "editor-events:#{@project_id}"
-				done()
-			return null
-
-		it "should not subscribe to the applied-ops channels anymore", (done) ->
-			rclient.pubsub 'CHANNELS', (err, resp) =>
-				return done(err) if err
-				resp.should.not.include "applied-ops:#{@doc_id}"
-				done()
-			return null
+					}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
+				},
+
+				cb => {
+					this.clientA = RealTimeClient.connect();
+					return this.clientA.on("connect", cb);
+				},
+
+				cb => {
+					return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
+						this.project = project;
+						this.privilegeLevel = privilegeLevel;
+						this.protocolVersion = protocolVersion;
+						return cb(error);
+					});
+				},
+
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
+				cb => {
+					return this.clientA.emit("joinDoc", this.doc_id, cb);
+				},
+
+				cb => {
+					// leaveProject is called when the client disconnects
+					this.clientA.on("disconnect", () => cb());
+					return this.clientA.disconnect();
+				},
+
+				cb => {
+					// The API waits a little while before flushing changes
+					return setTimeout(done, 1000);
+				}
+			], done);
+		});
+
+		it("should flush the project to the document updater", function() {
+			return MockDocUpdaterServer.deleteProject
+				.calledWith(this.project_id)
+				.should.equal(true);
+		});
+
+		it("should not subscribe to the editor-events channels anymore", function(done) {
+			rclient.pubsub('CHANNELS', (err, resp) => {
+				if (err) { return done(err); }
+				resp.should.not.include(`editor-events:${this.project_id}`);
+				return done();
+			});
+			return null;
+		});
+
+		return it("should not subscribe to the applied-ops channels anymore", function(done) {
+			rclient.pubsub('CHANNELS', (err, resp) => {
+				if (err) { return done(err); }
+				resp.should.not.include(`applied-ops:${this.doc_id}`);
+				return done();
+			});
+			return null;
+		});
+	});
+});

+ 265 - 193
services/real-time/test/acceptance/coffee/PubSubRace.js

@@ -1,205 +1,277 @@
-RealTimeClient = require "./helpers/RealTimeClient"
-MockDocUpdaterServer = require "./helpers/MockDocUpdaterServer"
-FixturesManager = require "./helpers/FixturesManager"
-
-async = require "async"
-
-settings = require "settings-sharelatex"
-redis = require "redis-sharelatex"
-rclient = redis.createClient(settings.redis.pubsub)
-
-describe "PubSubRace", ->
-	before (done) ->
-		MockDocUpdaterServer.run done
-
-	describe "when the client leaves a doc before joinDoc completes", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
-						privilegeLevel: "owner"
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const RealTimeClient = require("./helpers/RealTimeClient");
+const MockDocUpdaterServer = require("./helpers/MockDocUpdaterServer");
+const FixturesManager = require("./helpers/FixturesManager");
+
+const async = require("async");
+
+const settings = require("settings-sharelatex");
+const redis = require("redis-sharelatex");
+const rclient = redis.createClient(settings.redis.pubsub);
+
+describe("PubSubRace", function() {
+	before(done => MockDocUpdaterServer.run(done));
+
+	describe("when the client leaves a doc before joinDoc completes", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
+						privilegeLevel: "owner",
 						project: {
 							name: "Test Project"
 						}
-					}, (e, {@project_id, @user_id}) => cb()
-
-				(cb) =>
-					@clientA = RealTimeClient.connect()
-					@clientA.on "connect", cb
-
-				(cb) =>
-					@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
-						cb(error)
-
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-						cb(e)
-
-				(cb) =>
-					@clientA.emit "joinDoc", @doc_id, () ->
-					# leave before joinDoc completes
-					@clientA.emit "leaveDoc", @doc_id, cb
-
-				(cb) =>
-					# wait for subscribe and unsubscribe
-					setTimeout cb, 100
-			], done
-
-		it "should not subscribe to the applied-ops channels anymore", (done) ->
-			rclient.pubsub 'CHANNELS', (err, resp) =>
-				return done(err) if err
-				resp.should.not.include "applied-ops:#{@doc_id}"
-				done()
-			return null
-
-	describe "when the client emits joinDoc and leaveDoc requests frequently and leaves eventually", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
-						privilegeLevel: "owner"
+					}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
+				},
+
+				cb => {
+					this.clientA = RealTimeClient.connect();
+					return this.clientA.on("connect", cb);
+				},
+
+				cb => {
+					return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
+						this.project = project;
+						this.privilegeLevel = privilegeLevel;
+						this.protocolVersion = protocolVersion;
+						return cb(error);
+					});
+				},
+
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
+
+				cb => {
+					this.clientA.emit("joinDoc", this.doc_id, function() {});
+					// leave before joinDoc completes
+					return this.clientA.emit("leaveDoc", this.doc_id, cb);
+				},
+
+				cb => {
+					// wait for subscribe and unsubscribe
+					return setTimeout(cb, 100);
+				}
+			], done);
+		});
+
+		return it("should not subscribe to the applied-ops channels anymore", function(done) {
+			rclient.pubsub('CHANNELS', (err, resp) => {
+				if (err) { return done(err); }
+				resp.should.not.include(`applied-ops:${this.doc_id}`);
+				return done();
+			});
+			return null;
+		});
+	});
+
+	describe("when the client emits joinDoc and leaveDoc requests frequently and leaves eventually", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
+						privilegeLevel: "owner",
 						project: {
 							name: "Test Project"
 						}
-					}, (e, {@project_id, @user_id}) => cb()
-
-				(cb) =>
-					@clientA = RealTimeClient.connect()
-					@clientA.on "connect", cb
-
-				(cb) =>
-					@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
-						cb(error)
-
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-						cb(e)
-
-				(cb) =>
-					@clientA.emit "joinDoc", @doc_id, () ->
-					@clientA.emit "leaveDoc", @doc_id, () ->
-					@clientA.emit "joinDoc", @doc_id, () ->
-					@clientA.emit "leaveDoc", @doc_id, () ->
-					@clientA.emit "joinDoc", @doc_id, () ->
-					@clientA.emit "leaveDoc", @doc_id, () ->
-					@clientA.emit "joinDoc", @doc_id, () ->
-					@clientA.emit "leaveDoc", @doc_id, () ->
-					@clientA.emit "joinDoc", @doc_id, () ->
-					@clientA.emit "leaveDoc", @doc_id, cb
-
-				(cb) =>
-					# wait for subscribe and unsubscribe
-					setTimeout cb, 100
-			], done
-
-		it "should not subscribe to the applied-ops channels anymore", (done) ->
-			rclient.pubsub 'CHANNELS', (err, resp) =>
-				return done(err) if err
-				resp.should.not.include "applied-ops:#{@doc_id}"
-				done()
-			return null
-
-	describe "when the client emits joinDoc and leaveDoc requests frequently and remains in the doc", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
-						privilegeLevel: "owner"
+					}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
+				},
+
+				cb => {
+					this.clientA = RealTimeClient.connect();
+					return this.clientA.on("connect", cb);
+				},
+
+				cb => {
+					return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
+						this.project = project;
+						this.privilegeLevel = privilegeLevel;
+						this.protocolVersion = protocolVersion;
+						return cb(error);
+					});
+				},
+
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
+
+				cb => {
+					this.clientA.emit("joinDoc", this.doc_id, function() {});
+					this.clientA.emit("leaveDoc", this.doc_id, function() {});
+					this.clientA.emit("joinDoc", this.doc_id, function() {});
+					this.clientA.emit("leaveDoc", this.doc_id, function() {});
+					this.clientA.emit("joinDoc", this.doc_id, function() {});
+					this.clientA.emit("leaveDoc", this.doc_id, function() {});
+					this.clientA.emit("joinDoc", this.doc_id, function() {});
+					this.clientA.emit("leaveDoc", this.doc_id, function() {});
+					this.clientA.emit("joinDoc", this.doc_id, function() {});
+					return this.clientA.emit("leaveDoc", this.doc_id, cb);
+				},
+
+				cb => {
+					// wait for subscribe and unsubscribe
+					return setTimeout(cb, 100);
+				}
+			], done);
+		});
+
+		return it("should not subscribe to the applied-ops channels anymore", function(done) {
+			rclient.pubsub('CHANNELS', (err, resp) => {
+				if (err) { return done(err); }
+				resp.should.not.include(`applied-ops:${this.doc_id}`);
+				return done();
+			});
+			return null;
+		});
+	});
+
+	describe("when the client emits joinDoc and leaveDoc requests frequently and remains in the doc", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
+						privilegeLevel: "owner",
 						project: {
 							name: "Test Project"
 						}
-					}, (e, {@project_id, @user_id}) => cb()
-
-				(cb) =>
-					@clientA = RealTimeClient.connect()
-					@clientA.on "connect", cb
-
-				(cb) =>
-					@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
-						cb(error)
-
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-						cb(e)
-
-				(cb) =>
-					@clientA.emit "joinDoc", @doc_id, () ->
-					@clientA.emit "leaveDoc", @doc_id, () ->
-					@clientA.emit "joinDoc", @doc_id, () ->
-					@clientA.emit "leaveDoc", @doc_id, () ->
-					@clientA.emit "joinDoc", @doc_id, () ->
-					@clientA.emit "leaveDoc", @doc_id, () ->
-					@clientA.emit "joinDoc", @doc_id, () ->
-					@clientA.emit "leaveDoc", @doc_id, () ->
-					@clientA.emit "joinDoc", @doc_id, cb
-
-				(cb) =>
-					# wait for subscribe and unsubscribe
-					setTimeout cb, 100
-			], done
-
-		it "should subscribe to the applied-ops channels", (done) ->
-			rclient.pubsub 'CHANNELS', (err, resp) =>
-				return done(err) if err
-				resp.should.include "applied-ops:#{@doc_id}"
-				done()
-			return null
-
-	describe "when the client disconnects before joinDoc completes", ->
-		before (done) ->
-			async.series [
-				(cb) =>
-					FixturesManager.setUpProject {
-						privilegeLevel: "owner"
+					}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
+				},
+
+				cb => {
+					this.clientA = RealTimeClient.connect();
+					return this.clientA.on("connect", cb);
+				},
+
+				cb => {
+					return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
+						this.project = project;
+						this.privilegeLevel = privilegeLevel;
+						this.protocolVersion = protocolVersion;
+						return cb(error);
+					});
+				},
+
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
+
+				cb => {
+					this.clientA.emit("joinDoc", this.doc_id, function() {});
+					this.clientA.emit("leaveDoc", this.doc_id, function() {});
+					this.clientA.emit("joinDoc", this.doc_id, function() {});
+					this.clientA.emit("leaveDoc", this.doc_id, function() {});
+					this.clientA.emit("joinDoc", this.doc_id, function() {});
+					this.clientA.emit("leaveDoc", this.doc_id, function() {});
+					this.clientA.emit("joinDoc", this.doc_id, function() {});
+					this.clientA.emit("leaveDoc", this.doc_id, function() {});
+					return this.clientA.emit("joinDoc", this.doc_id, cb);
+				},
+
+				cb => {
+					// wait for subscribe and unsubscribe
+					return setTimeout(cb, 100);
+				}
+			], done);
+		});
+
+		return it("should subscribe to the applied-ops channels", function(done) {
+			rclient.pubsub('CHANNELS', (err, resp) => {
+				if (err) { return done(err); }
+				resp.should.include(`applied-ops:${this.doc_id}`);
+				return done();
+			});
+			return null;
+		});
+	});
+
+	return describe("when the client disconnects before joinDoc completes", function() {
+		before(function(done) {
+			return async.series([
+				cb => {
+					return FixturesManager.setUpProject({
+						privilegeLevel: "owner",
 						project: {
 							name: "Test Project"
 						}
-					}, (e, {@project_id, @user_id}) => cb()
-
-				(cb) =>
-					@clientA = RealTimeClient.connect()
-					@clientA.on "connect", cb
-
-				(cb) =>
-					@clientA.emit "joinProject", project_id: @project_id, (error, @project, @privilegeLevel, @protocolVersion) =>
-						cb(error)
-
-				(cb) =>
-					FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-						cb(e)
-
-				(cb) =>
-					joinDocCompleted = false
-					@clientA.emit "joinDoc", @doc_id, () ->
-						joinDocCompleted = true
-					# leave before joinDoc completes
-					setTimeout () =>
-						if joinDocCompleted
-							return cb(new Error('joinDocCompleted -- lower timeout'))
-						@clientA.on "disconnect", () -> cb()
-						@clientA.disconnect()
-					# socket.io processes joinDoc and disconnect with different delays:
-					#  - joinDoc goes through two process.nextTick
-					#  - disconnect goes through one process.nextTick
-					# We have to inject the disconnect event into a different event loop
-					#  cycle.
-					, 3
-
-				(cb) =>
-					# wait for subscribe and unsubscribe
-					setTimeout cb, 100
-			], done
-
-		it "should not subscribe to the editor-events channels anymore", (done) ->
-			rclient.pubsub 'CHANNELS', (err, resp) =>
-				return done(err) if err
-				resp.should.not.include "editor-events:#{@project_id}"
-				done()
-			return null
-
-		it "should not subscribe to the applied-ops channels anymore", (done) ->
-			rclient.pubsub 'CHANNELS', (err, resp) =>
-				return done(err) if err
-				resp.should.not.include "applied-ops:#{@doc_id}"
-				done()
-			return null
+					}, (e, {project_id, user_id}) => { this.project_id = project_id; this.user_id = user_id; return cb(); });
+				},
+
+				cb => {
+					this.clientA = RealTimeClient.connect();
+					return this.clientA.on("connect", cb);
+				},
+
+				cb => {
+					return this.clientA.emit("joinProject", {project_id: this.project_id}, (error, project, privilegeLevel, protocolVersion) => {
+						this.project = project;
+						this.privilegeLevel = privilegeLevel;
+						this.protocolVersion = protocolVersion;
+						return cb(error);
+					});
+				},
+
+				cb => {
+					return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+						this.doc_id = doc_id;
+						return cb(e);
+					});
+				},
+
+				cb => {
+					let joinDocCompleted = false;
+					this.clientA.emit("joinDoc", this.doc_id, () => joinDocCompleted = true);
+					// leave before joinDoc completes
+					return setTimeout(() => {
+						if (joinDocCompleted) {
+							return cb(new Error('joinDocCompleted -- lower timeout'));
+						}
+						this.clientA.on("disconnect", () => cb());
+						return this.clientA.disconnect();
+					}
+					// socket.io processes joinDoc and disconnect with different delays:
+					//  - joinDoc goes through two process.nextTick
+					//  - disconnect goes through one process.nextTick
+					// We have to inject the disconnect event into a different event loop
+					//  cycle.
+					, 3);
+				},
+
+				cb => {
+					// wait for subscribe and unsubscribe
+					return setTimeout(cb, 100);
+				}
+			], done);
+		});
+
+		it("should not subscribe to the editor-events channels anymore", function(done) {
+			rclient.pubsub('CHANNELS', (err, resp) => {
+				if (err) { return done(err); }
+				resp.should.not.include(`editor-events:${this.project_id}`);
+				return done();
+			});
+			return null;
+		});
+
+		return it("should not subscribe to the applied-ops channels anymore", function(done) {
+			rclient.pubsub('CHANNELS', (err, resp) => {
+				if (err) { return done(err); }
+				resp.should.not.include(`applied-ops:${this.doc_id}`);
+				return done();
+			});
+			return null;
+		});
+	});
+});

+ 240 - 174
services/real-time/test/acceptance/coffee/ReceiveUpdateTests.js

@@ -1,208 +1,274 @@
-chai = require("chai")
-expect = chai.expect
-chai.should()
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const chai = require("chai");
+const {
+    expect
+} = chai;
+chai.should();
 
-RealTimeClient = require "./helpers/RealTimeClient"
-MockWebServer = require "./helpers/MockWebServer"
-FixturesManager = require "./helpers/FixturesManager"
+const RealTimeClient = require("./helpers/RealTimeClient");
+const MockWebServer = require("./helpers/MockWebServer");
+const FixturesManager = require("./helpers/FixturesManager");
 
-async = require "async"
+const async = require("async");
 
-settings = require "settings-sharelatex"
-redis = require "redis-sharelatex"
-rclient = redis.createClient(settings.redis.pubsub)
+const settings = require("settings-sharelatex");
+const redis = require("redis-sharelatex");
+const rclient = redis.createClient(settings.redis.pubsub);
 
-describe "receiveUpdate", ->
-	beforeEach (done) ->
-		@lines = ["test", "doc", "lines"]
-		@version = 42
-		@ops = ["mock", "doc", "ops"]
+describe("receiveUpdate", function() {
+	beforeEach(function(done) {
+		this.lines = ["test", "doc", "lines"];
+		this.version = 42;
+		this.ops = ["mock", "doc", "ops"];
 		
-		async.series [
-			(cb) =>
-				FixturesManager.setUpProject {
-					privilegeLevel: "owner"
+		return async.series([
+			cb => {
+				return FixturesManager.setUpProject({
+					privilegeLevel: "owner",
 					project: { name: "Test Project"	}
-				}, (error, {@user_id, @project_id}) => cb()
+				}, (error, {user_id, project_id}) => { this.user_id = user_id; this.project_id = project_id; return cb(); });
+			},
 			
-			(cb) =>
-				FixturesManager.setUpDoc @project_id, {@lines, @version, @ops}, (e, {@doc_id}) =>
-					cb(e)
+			cb => {
+				return FixturesManager.setUpDoc(this.project_id, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id}) => {
+					this.doc_id = doc_id;
+					return cb(e);
+				});
+			},
 			
-			(cb) =>
-				@clientA = RealTimeClient.connect()
-				@clientA.on "connectionAccepted", cb
+			cb => {
+				this.clientA = RealTimeClient.connect();
+				return this.clientA.on("connectionAccepted", cb);
+			},
 				
-			(cb) =>
-				@clientB = RealTimeClient.connect()
-				@clientB.on "connectionAccepted", cb
+			cb => {
+				this.clientB = RealTimeClient.connect();
+				return this.clientB.on("connectionAccepted", cb);
+			},
 				
-			(cb) =>
-				@clientA.emit "joinProject", {
-					project_id: @project_id
-				}, cb
+			cb => {
+				return this.clientA.emit("joinProject", {
+					project_id: this.project_id
+				}, cb);
+			},
 			
-			(cb) =>
-				@clientA.emit "joinDoc", @doc_id, cb
+			cb => {
+				return this.clientA.emit("joinDoc", this.doc_id, cb);
+			},
 				
-			(cb) =>
-				@clientB.emit "joinProject", {
-					project_id: @project_id
-				}, cb
+			cb => {
+				return this.clientB.emit("joinProject", {
+					project_id: this.project_id
+				}, cb);
+			},
 			
-			(cb) =>
-				@clientB.emit "joinDoc", @doc_id, cb
+			cb => {
+				return this.clientB.emit("joinDoc", this.doc_id, cb);
+			},
 
-			(cb) =>
-				FixturesManager.setUpProject {
-					privilegeLevel: "owner"
+			cb => {
+				return FixturesManager.setUpProject({
+					privilegeLevel: "owner",
 					project: {name: "Test Project"}
-				}, (error, {user_id: @user_id_second, project_id: @project_id_second}) => cb()
-
-			(cb) =>
-				FixturesManager.setUpDoc @project_id_second, {@lines, @version, @ops}, (e, {doc_id: @doc_id_second}) =>
-					cb(e)
-
-			(cb) =>
-				@clientC = RealTimeClient.connect()
-				@clientC.on "connectionAccepted", cb
-
-			(cb) =>
-				@clientC.emit "joinProject", {
-					project_id: @project_id_second
-				}, cb
-			(cb) =>
-				@clientC.emit "joinDoc", @doc_id_second, cb
-
-			(cb) =>
-				@clientAUpdates = []
-				@clientA.on "otUpdateApplied", (update) => @clientAUpdates.push(update)
-				@clientBUpdates = []
-				@clientB.on "otUpdateApplied", (update) => @clientBUpdates.push(update)
-				@clientCUpdates = []
-				@clientC.on "otUpdateApplied", (update) => @clientCUpdates.push(update)
-
-				@clientAErrors = []
-				@clientA.on "otUpdateError", (error) => @clientAErrors.push(error)
-				@clientBErrors = []
-				@clientB.on "otUpdateError", (error) => @clientBErrors.push(error)
-				@clientCErrors = []
-				@clientC.on "otUpdateError", (error) => @clientCErrors.push(error)
-				cb()
-		], done
-
-	afterEach () ->
-		@clientA?.disconnect()
-		@clientB?.disconnect()
-		@clientC?.disconnect()
-
-	describe "with an update from clientA", ->
-		beforeEach (done) ->
-			@update = {
-				doc_id: @doc_id
-				op:
-					meta:
-						source: @clientA.publicId
-					v: @version
-					doc: @doc_id
-					op: [{i: "foo", p: 50}]				
+				}, (error, {user_id: user_id_second, project_id: project_id_second}) => { this.user_id_second = user_id_second; this.project_id_second = project_id_second; return cb(); });
+			},
+
+			cb => {
+				return FixturesManager.setUpDoc(this.project_id_second, {lines: this.lines, version: this.version, ops: this.ops}, (e, {doc_id: doc_id_second}) => {
+					this.doc_id_second = doc_id_second;
+					return cb(e);
+				});
+			},
+
+			cb => {
+				this.clientC = RealTimeClient.connect();
+				return this.clientC.on("connectionAccepted", cb);
+			},
+
+			cb => {
+				return this.clientC.emit("joinProject", {
+					project_id: this.project_id_second
+				}, cb);
+			},
+			cb => {
+				return this.clientC.emit("joinDoc", this.doc_id_second, cb);
+			},
+
+			cb => {
+				this.clientAUpdates = [];
+				this.clientA.on("otUpdateApplied", update => this.clientAUpdates.push(update));
+				this.clientBUpdates = [];
+				this.clientB.on("otUpdateApplied", update => this.clientBUpdates.push(update));
+				this.clientCUpdates = [];
+				this.clientC.on("otUpdateApplied", update => this.clientCUpdates.push(update));
+
+				this.clientAErrors = [];
+				this.clientA.on("otUpdateError", error => this.clientAErrors.push(error));
+				this.clientBErrors = [];
+				this.clientB.on("otUpdateError", error => this.clientBErrors.push(error));
+				this.clientCErrors = [];
+				this.clientC.on("otUpdateError", error => this.clientCErrors.push(error));
+				return cb();
 			}
-			rclient.publish "applied-ops", JSON.stringify(@update)
-			setTimeout done, 200 # Give clients time to get message
+		], done);
+	});
+
+	afterEach(function() {
+		if (this.clientA != null) {
+			this.clientA.disconnect();
+		}
+		if (this.clientB != null) {
+			this.clientB.disconnect();
+		}
+		return (this.clientC != null ? this.clientC.disconnect() : undefined);
+	});
+
+	describe("with an update from clientA", function() {
+		beforeEach(function(done) {
+			this.update = {
+				doc_id: this.doc_id,
+				op: {
+					meta: {
+						source: this.clientA.publicId
+					},
+					v: this.version,
+					doc: this.doc_id,
+					op: [{i: "foo", p: 50}]
+				}				
+			};
+			rclient.publish("applied-ops", JSON.stringify(this.update));
+			return setTimeout(done, 200);
+		}); // Give clients time to get message
 			
-		it "should send the full op to clientB", ->
-			@clientBUpdates.should.deep.equal [@update.op]
+		it("should send the full op to clientB", function() {
+			return this.clientBUpdates.should.deep.equal([this.update.op]);
+	});
 			
-		it "should send an ack to clientA", ->
-			@clientAUpdates.should.deep.equal [{
-				v: @version, doc: @doc_id
-			}]
-
-		it "should send nothing to clientC", ->
-			@clientCUpdates.should.deep.equal []
-
-	describe "with an update from clientC", ->
-		beforeEach (done) ->
-			@update = {
-				doc_id: @doc_id_second
-				op:
-					meta:
-						source: @clientC.publicId
-					v: @version
-					doc: @doc_id_second
+		it("should send an ack to clientA", function() {
+			return this.clientAUpdates.should.deep.equal([{
+				v: this.version, doc: this.doc_id
+			}]);
+	});
+
+		return it("should send nothing to clientC", function() {
+			return this.clientCUpdates.should.deep.equal([]);
+	});
+});
+
+	describe("with an update from clientC", function() {
+		beforeEach(function(done) {
+			this.update = {
+				doc_id: this.doc_id_second,
+				op: {
+					meta: {
+						source: this.clientC.publicId
+					},
+					v: this.version,
+					doc: this.doc_id_second,
 					op: [{i: "update from clientC", p: 50}]
-			}
-			rclient.publish "applied-ops", JSON.stringify(@update)
-			setTimeout done, 200 # Give clients time to get message
-
-		it "should send nothing to clientA", ->
-			@clientAUpdates.should.deep.equal []
-
-		it "should send nothing to clientB", ->
-			@clientBUpdates.should.deep.equal []
-
-		it "should send an ack to clientC", ->
-			@clientCUpdates.should.deep.equal [{
-				v: @version, doc: @doc_id_second
-			}]
-
-	describe "with an update from a remote client for project 1", ->
-		beforeEach (done) ->
-			@update = {
-				doc_id: @doc_id
-				op:
-					meta:
+				}
+			};
+			rclient.publish("applied-ops", JSON.stringify(this.update));
+			return setTimeout(done, 200);
+		}); // Give clients time to get message
+
+		it("should send nothing to clientA", function() {
+			return this.clientAUpdates.should.deep.equal([]);
+	});
+
+		it("should send nothing to clientB", function() {
+			return this.clientBUpdates.should.deep.equal([]);
+	});
+
+		return it("should send an ack to clientC", function() {
+			return this.clientCUpdates.should.deep.equal([{
+				v: this.version, doc: this.doc_id_second
+			}]);
+	});
+});
+
+	describe("with an update from a remote client for project 1", function() {
+		beforeEach(function(done) {
+			this.update = {
+				doc_id: this.doc_id,
+				op: {
+					meta: {
 						source: 'this-is-a-remote-client-id'
-					v: @version
-					doc: @doc_id
+					},
+					v: this.version,
+					doc: this.doc_id,
 					op: [{i: "foo", p: 50}]
-			}
-			rclient.publish "applied-ops", JSON.stringify(@update)
-			setTimeout done, 200 # Give clients time to get message
+				}
+			};
+			rclient.publish("applied-ops", JSON.stringify(this.update));
+			return setTimeout(done, 200);
+		}); // Give clients time to get message
 
-		it "should send the full op to clientA", ->
-			@clientAUpdates.should.deep.equal [@update.op]
+		it("should send the full op to clientA", function() {
+			return this.clientAUpdates.should.deep.equal([this.update.op]);
+	});
 			
-		it "should send the full op to clientB", ->
-			@clientBUpdates.should.deep.equal [@update.op]
+		it("should send the full op to clientB", function() {
+			return this.clientBUpdates.should.deep.equal([this.update.op]);
+	});
 
-		it "should send nothing to clientC", ->
-			@clientCUpdates.should.deep.equal []
+		return it("should send nothing to clientC", function() {
+			return this.clientCUpdates.should.deep.equal([]);
+	});
+});
 
-	describe "with an error for the first project", ->
-		beforeEach (done) ->
-			rclient.publish "applied-ops", JSON.stringify({doc_id: @doc_id, error: @error = "something went wrong"})
-			setTimeout done, 200 # Give clients time to get message
+	describe("with an error for the first project", function() {
+		beforeEach(function(done) {
+			rclient.publish("applied-ops", JSON.stringify({doc_id: this.doc_id, error: (this.error = "something went wrong")}));
+			return setTimeout(done, 200);
+		}); // Give clients time to get message
 
-		it "should send the error to the clients in the first project", ->
-			@clientAErrors.should.deep.equal [@error]
-			@clientBErrors.should.deep.equal [@error]
+		it("should send the error to the clients in the first project", function() {
+			this.clientAErrors.should.deep.equal([this.error]);
+			return this.clientBErrors.should.deep.equal([this.error]);
+	});
 
-		it "should not send any errors to the client in the second project", ->
-			@clientCErrors.should.deep.equal []
+		it("should not send any errors to the client in the second project", function() {
+			return this.clientCErrors.should.deep.equal([]);
+	});
 
-		it "should disconnect the clients of the first project", ->
-			@clientA.socket.connected.should.equal false
-			@clientB.socket.connected.should.equal false
+		it("should disconnect the clients of the first project", function() {
+			this.clientA.socket.connected.should.equal(false);
+			return this.clientB.socket.connected.should.equal(false);
+		});
 
-		it "should not disconnect the client in the second project", ->
-			@clientC.socket.connected.should.equal true
+		return it("should not disconnect the client in the second project", function() {
+			return this.clientC.socket.connected.should.equal(true);
+		});
+	});
 
-	describe "with an error for the second project", ->
-		beforeEach (done) ->
-			rclient.publish "applied-ops", JSON.stringify({doc_id: @doc_id_second, error: @error = "something went wrong"})
-			setTimeout done, 200 # Give clients time to get message
+	return describe("with an error for the second project", function() {
+		beforeEach(function(done) {
+			rclient.publish("applied-ops", JSON.stringify({doc_id: this.doc_id_second, error: (this.error = "something went wrong")}));
+			return setTimeout(done, 200);
+		}); // Give clients time to get message
 
-		it "should not send any errors to the clients in the first project", ->
-			@clientAErrors.should.deep.equal []
-			@clientBErrors.should.deep.equal []
+		it("should not send any errors to the clients in the first project", function() {
+			this.clientAErrors.should.deep.equal([]);
+			return this.clientBErrors.should.deep.equal([]);
+	});
 
-		it "should send the error to the client in the second project", ->
-			@clientCErrors.should.deep.equal [@error]
+		it("should send the error to the client in the second project", function() {
+			return this.clientCErrors.should.deep.equal([this.error]);
+	});
 
-		it "should not disconnect the clients of the first project", ->
-			@clientA.socket.connected.should.equal true
-			@clientB.socket.connected.should.equal true
+		it("should not disconnect the clients of the first project", function() {
+			this.clientA.socket.connected.should.equal(true);
+			return this.clientB.socket.connected.should.equal(true);
+		});
 
-		it "should disconnect the client in the second project", ->
-			@clientC.socket.connected.should.equal false
+		return it("should disconnect the client in the second project", function() {
+			return this.clientC.socket.connected.should.equal(false);
+		});
+	});
+});

+ 87 - 62
services/real-time/test/acceptance/coffee/RouterTests.js

@@ -1,76 +1,101 @@
-async = require "async"
-{expect} = require("chai")
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const async = require("async");
+const {expect} = require("chai");
 
-RealTimeClient = require "./helpers/RealTimeClient"
-FixturesManager = require "./helpers/FixturesManager"
+const RealTimeClient = require("./helpers/RealTimeClient");
+const FixturesManager = require("./helpers/FixturesManager");
 
 
-describe "Router", ->
-	describe "joinProject", ->
-		describe "when there is no callback provided", ->
-			after () ->
-				process.removeListener('unhandledRejection', @onUnhandled)
+describe("Router", () => describe("joinProject", function() {
+    describe("when there is no callback provided", function() {
+        after(function() {
+            return process.removeListener('unhandledRejection', this.onUnhandled);
+        });
 
-			before (done) ->
-				@onUnhandled = (error) ->
-					done(error)
-				process.on('unhandledRejection', @onUnhandled)
-				async.series [
-					(cb) =>
-						FixturesManager.setUpProject {
-							privilegeLevel: "owner"
-							project: {
-								name: "Test Project"
-							}
-						}, (e, {@project_id, @user_id}) =>
-							cb(e)
+        before(function(done) {
+            this.onUnhandled = error => done(error);
+            process.on('unhandledRejection', this.onUnhandled);
+            return async.series([
+                cb => {
+                    return FixturesManager.setUpProject({
+                        privilegeLevel: "owner",
+                        project: {
+                            name: "Test Project"
+                        }
+                    }, (e, {project_id, user_id}) => {
+                        this.project_id = project_id;
+                        this.user_id = user_id;
+                        return cb(e);
+                    });
+                },
 
-					(cb) =>
-						@client = RealTimeClient.connect()
-						@client.on "connectionAccepted", cb
+                cb => {
+                    this.client = RealTimeClient.connect();
+                    return this.client.on("connectionAccepted", cb);
+                },
 
-					(cb) =>
-						@client = RealTimeClient.connect()
-						@client.on "connectionAccepted", cb
+                cb => {
+                    this.client = RealTimeClient.connect();
+                    return this.client.on("connectionAccepted", cb);
+                },
 
-					(cb) =>
-						@client.emit "joinProject", project_id: @project_id
-						setTimeout(cb, 100)
-				], done
+                cb => {
+                    this.client.emit("joinProject", {project_id: this.project_id});
+                    return setTimeout(cb, 100);
+                }
+            ], done);
+        });
 
-			it "should keep on going", ->
-				expect('still running').to.exist
+        return it("should keep on going", () => expect('still running').to.exist);
+    });
 
-		describe "when there are too many arguments", ->
-			after () ->
-				process.removeListener('unhandledRejection', @onUnhandled)
+    return describe("when there are too many arguments", function() {
+        after(function() {
+            return process.removeListener('unhandledRejection', this.onUnhandled);
+        });
 
-			before (done) ->
-				@onUnhandled = (error) ->
-					done(error)
-				process.on('unhandledRejection', @onUnhandled)
-				async.series [
-					(cb) =>
-						FixturesManager.setUpProject {
-							privilegeLevel: "owner"
-							project: {
-								name: "Test Project"
-							}
-						}, (e, {@project_id, @user_id}) =>
-							cb(e)
+        before(function(done) {
+            this.onUnhandled = error => done(error);
+            process.on('unhandledRejection', this.onUnhandled);
+            return async.series([
+                cb => {
+                    return FixturesManager.setUpProject({
+                        privilegeLevel: "owner",
+                        project: {
+                            name: "Test Project"
+                        }
+                    }, (e, {project_id, user_id}) => {
+                        this.project_id = project_id;
+                        this.user_id = user_id;
+                        return cb(e);
+                    });
+                },
 
-					(cb) =>
-						@client = RealTimeClient.connect()
-						@client.on "connectionAccepted", cb
+                cb => {
+                    this.client = RealTimeClient.connect();
+                    return this.client.on("connectionAccepted", cb);
+                },
 
-					(cb) =>
-						@client = RealTimeClient.connect()
-						@client.on "connectionAccepted", cb
+                cb => {
+                    this.client = RealTimeClient.connect();
+                    return this.client.on("connectionAccepted", cb);
+                },
 
-					(cb) =>
-						@client.emit "joinProject", 1, 2, 3, 4, 5, (@error) =>
-							cb()
-				], done
+                cb => {
+                    return this.client.emit("joinProject", 1, 2, 3, 4, 5, error => {
+                        this.error = error;
+                        return cb();
+                    });
+                }
+            ], done);
+        });
 
-			it "should return an error message", ->
-				expect(@error.message).to.equal('unexpected arguments')
+        return it("should return an error message", function() {
+            return expect(this.error.message).to.equal('unexpected arguments');
+        });
+    });
+}));

+ 78 - 55
services/real-time/test/acceptance/coffee/SessionSocketsTests.js

@@ -1,67 +1,90 @@
-RealTimeClient = require("./helpers/RealTimeClient")
-Settings = require("settings-sharelatex")
-{expect} = require('chai')
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const RealTimeClient = require("./helpers/RealTimeClient");
+const Settings = require("settings-sharelatex");
+const {expect} = require('chai');
 
-describe 'SessionSockets', ->
-	before ->
-		@checkSocket = (fn) ->
-			client = RealTimeClient.connect()
-			client.on 'connectionAccepted', fn
-			client.on 'connectionRejected', fn
-			return null
+describe('SessionSockets', function() {
+	before(function() {
+		return this.checkSocket = function(fn) {
+			const client = RealTimeClient.connect();
+			client.on('connectionAccepted', fn);
+			client.on('connectionRejected', fn);
+			return null;
+		};
+	});
 
-	describe 'without cookies', ->
-		before ->
-			RealTimeClient.cookie = null
+	describe('without cookies', function() {
+		before(() => RealTimeClient.cookie = null);
 
-		it 'should return a lookup error', (done) ->
-			@checkSocket (error) ->
-				expect(error).to.exist
-				expect(error.message).to.equal('invalid session')
-				done()
+		return it('should return a lookup error', function(done) {
+			return this.checkSocket(function(error) {
+				expect(error).to.exist;
+				expect(error.message).to.equal('invalid session');
+				return done();
+			});
+		});
+	});
 
-	describe 'with a different cookie', ->
-		before ->
-			RealTimeClient.cookie = "some.key=someValue"
+	describe('with a different cookie', function() {
+		before(() => RealTimeClient.cookie = "some.key=someValue");
 
-		it 'should return a lookup error', (done) ->
-			@checkSocket (error) ->
-				expect(error).to.exist
-				expect(error.message).to.equal('invalid session')
-				done()
+		return it('should return a lookup error', function(done) {
+			return this.checkSocket(function(error) {
+				expect(error).to.exist;
+				expect(error.message).to.equal('invalid session');
+				return done();
+			});
+		});
+	});
 
-	describe 'with an invalid cookie', ->
-		before (done) ->
-			RealTimeClient.setSession {}, (error) ->
-				return done(error) if error
-				RealTimeClient.cookie = "#{Settings.cookieName}=#{
+	describe('with an invalid cookie', function() {
+		before(function(done) {
+			RealTimeClient.setSession({}, function(error) {
+				if (error) { return done(error); }
+				RealTimeClient.cookie = `${Settings.cookieName}=${
 					RealTimeClient.cookie.slice(17, 49)
-				}"
-				done()
-			return null
+				}`;
+				return done();
+			});
+			return null;
+		});
 
-		it 'should return a lookup error', (done) ->
-			@checkSocket (error) ->
-				expect(error).to.exist
-				expect(error.message).to.equal('invalid session')
-				done()
+		return it('should return a lookup error', function(done) {
+			return this.checkSocket(function(error) {
+				expect(error).to.exist;
+				expect(error.message).to.equal('invalid session');
+				return done();
+			});
+		});
+	});
 
-	describe 'with a valid cookie and no matching session', ->
-		before ->
-			RealTimeClient.cookie = "#{Settings.cookieName}=unknownId"
+	describe('with a valid cookie and no matching session', function() {
+		before(() => RealTimeClient.cookie = `${Settings.cookieName}=unknownId`);
 
-		it 'should return a lookup error', (done) ->
-			@checkSocket (error) ->
-				expect(error).to.exist
-				expect(error.message).to.equal('invalid session')
-				done()
+		return it('should return a lookup error', function(done) {
+			return this.checkSocket(function(error) {
+				expect(error).to.exist;
+				expect(error.message).to.equal('invalid session');
+				return done();
+			});
+		});
+	});
 
-	describe 'with a valid cookie and a matching session', ->
-		before (done) ->
-			RealTimeClient.setSession({}, done)
-			return null
+	return describe('with a valid cookie and a matching session', function() {
+		before(function(done) {
+			RealTimeClient.setSession({}, done);
+			return null;
+		});
 
-		it 'should not return an error', (done) ->
-			@checkSocket (error) ->
-				expect(error).to.not.exist
-				done()
+		return it('should not return an error', function(done) {
+			return this.checkSocket(function(error) {
+				expect(error).to.not.exist;
+				return done();
+			});
+		});
+	});
+});

+ 48 - 32
services/real-time/test/acceptance/coffee/SessionTests.js

@@ -1,35 +1,51 @@
-chai = require("chai")
-expect = chai.expect
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const chai = require("chai");
+const {
+    expect
+} = chai;
 
-RealTimeClient = require "./helpers/RealTimeClient"
+const RealTimeClient = require("./helpers/RealTimeClient");
 
-describe "Session", ->
-	describe "with an established session", ->
-		before (done) ->
-			@user_id = "mock-user-id"
-			RealTimeClient.setSession {
-				user: { _id: @user_id }
-			}, (error) =>
-				throw error if error?
-				@client = RealTimeClient.connect()
-				return done()
-			return null
+describe("Session", () => describe("with an established session", function() {
+    before(function(done) {
+        this.user_id = "mock-user-id";
+        RealTimeClient.setSession({
+            user: { _id: this.user_id }
+        }, error => {
+            if (error != null) { throw error; }
+            this.client = RealTimeClient.connect();
+            return done();
+        });
+        return null;
+    });
 
-		it "should not get disconnected", (done) ->
-			disconnected = false
-			@client.on "disconnect", () ->
-				disconnected = true
-			setTimeout () =>
-				expect(disconnected).to.equal false
-				done()
-			, 500
-			
-		it "should appear in the list of connected clients", (done) ->
-			RealTimeClient.getConnectedClients (error, clients) =>
-				included = false
-				for client in clients
-					if client.client_id == @client.socket.sessionid
-						included = true
-						break
-				expect(included).to.equal true
-				done()
+    it("should not get disconnected", function(done) {
+        let disconnected = false;
+        this.client.on("disconnect", () => disconnected = true);
+        return setTimeout(() => {
+            expect(disconnected).to.equal(false);
+            return done();
+        }
+        , 500);
+    });
+        
+    return it("should appear in the list of connected clients", function(done) {
+        return RealTimeClient.getConnectedClients((error, clients) => {
+            let included = false;
+            for (let client of Array.from(clients)) {
+                if (client.client_id === this.client.socket.sessionid) {
+                    included = true;
+                    break;
+                }
+            }
+            expect(included).to.equal(true);
+            return done();
+        });
+    });
+}));

+ 53 - 34
services/real-time/test/acceptance/coffee/helpers/FixturesManager.js

@@ -1,48 +1,67 @@
-RealTimeClient = require "./RealTimeClient"
-MockWebServer = require "./MockWebServer"
-MockDocUpdaterServer = require "./MockDocUpdaterServer"
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let FixturesManager;
+const RealTimeClient = require("./RealTimeClient");
+const MockWebServer = require("./MockWebServer");
+const MockDocUpdaterServer = require("./MockDocUpdaterServer");
 
-module.exports = FixturesManager =
-	setUpProject: (options = {}, callback = (error, data) ->) ->
-		options.user_id        ||= FixturesManager.getRandomId()
-		options.project_id     ||= FixturesManager.getRandomId()
-		options.project        ||= { name: "Test Project" }
-		{project_id, user_id, privilegeLevel, project, publicAccess} = options
+module.exports = (FixturesManager = {
+	setUpProject(options, callback) {
+		if (options == null) { options = {}; }
+		if (callback == null) { callback = function(error, data) {}; }
+		if (!options.user_id) { options.user_id = FixturesManager.getRandomId(); }
+		if (!options.project_id) { options.project_id = FixturesManager.getRandomId(); }
+		if (!options.project) { options.project = { name: "Test Project" }; }
+		const {project_id, user_id, privilegeLevel, project, publicAccess} = options;
 		
-		privileges = {}
-		privileges[user_id] = privilegeLevel
-		if publicAccess
-			privileges["anonymous-user"] = publicAccess
+		const privileges = {};
+		privileges[user_id] = privilegeLevel;
+		if (publicAccess) {
+			privileges["anonymous-user"] = publicAccess;
+		}
 		
-		MockWebServer.createMockProject(project_id, privileges, project)
-		MockWebServer.run (error) =>
-			throw error if error?
-			RealTimeClient.setSession {
+		MockWebServer.createMockProject(project_id, privileges, project);
+		return MockWebServer.run(error => {
+			if (error != null) { throw error; }
+			return RealTimeClient.setSession({
 				user: {
-					_id: user_id
-					first_name: "Joe"
+					_id: user_id,
+					first_name: "Joe",
 					last_name: "Bloggs"
 				}
-			}, (error) =>
-				throw error if error?
-				callback null, {project_id, user_id, privilegeLevel, project}
+			}, error => {
+				if (error != null) { throw error; }
+				return callback(null, {project_id, user_id, privilegeLevel, project});
+		});
+	});
+	},
 				
-	setUpDoc: (project_id, options = {}, callback = (error, data) ->) ->
-		options.doc_id  ||= FixturesManager.getRandomId()
-		options.lines   ||= ["doc", "lines"]
-		options.version ||= 42
-		options.ops     ||= ["mock", "ops"]
-		{doc_id, lines, version, ops, ranges} = options
+	setUpDoc(project_id, options, callback) {
+		if (options == null) { options = {}; }
+		if (callback == null) { callback = function(error, data) {}; }
+		if (!options.doc_id) { options.doc_id = FixturesManager.getRandomId(); }
+		if (!options.lines) { options.lines = ["doc", "lines"]; }
+		if (!options.version) { options.version = 42; }
+		if (!options.ops) { options.ops = ["mock", "ops"]; }
+		const {doc_id, lines, version, ops, ranges} = options;
 				
-		MockDocUpdaterServer.createMockDoc project_id, doc_id, {lines, version, ops, ranges}
-		MockDocUpdaterServer.run (error) =>
-			throw error if error?
-			callback null, {project_id, doc_id, lines, version, ops}
+		MockDocUpdaterServer.createMockDoc(project_id, doc_id, {lines, version, ops, ranges});
+		return MockDocUpdaterServer.run(error => {
+			if (error != null) { throw error; }
+			return callback(null, {project_id, doc_id, lines, version, ops});
+	});
+	},
 		
-	getRandomId: () ->
+	getRandomId() {
 		return require("crypto")
 			.createHash("sha1")
 			.update(Math.random().toString())
 			.digest("hex")
-			.slice(0,24)
+			.slice(0,24);
+	}
+});
 		

+ 56 - 37
services/real-time/test/acceptance/coffee/helpers/MockDocUpdaterServer.js

@@ -1,46 +1,65 @@
-sinon = require "sinon"
-express = require "express"
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let MockDocUpdaterServer;
+const sinon = require("sinon");
+const express = require("express");
 
-module.exports = MockDocUpdaterServer =
-	docs: {}
+module.exports = (MockDocUpdaterServer = {
+	docs: {},
 	
-	createMockDoc: (project_id, doc_id, data) ->
-		MockDocUpdaterServer.docs["#{project_id}:#{doc_id}"] = data
+	createMockDoc(project_id, doc_id, data) {
+		return MockDocUpdaterServer.docs[`${project_id}:${doc_id}`] = data;
+	},
 		
-	getDocument: (project_id, doc_id, fromVersion, callback = (error, data) ->) ->
-		callback(
-			null, MockDocUpdaterServer.docs["#{project_id}:#{doc_id}"]
-		)
+	getDocument(project_id, doc_id, fromVersion, callback) {
+		if (callback == null) { callback = function(error, data) {}; }
+		return callback(
+			null, MockDocUpdaterServer.docs[`${project_id}:${doc_id}`]
+		);
+	},
 		
-	deleteProject: sinon.stub().callsArg(1)
+	deleteProject: sinon.stub().callsArg(1),
 		
-	getDocumentRequest: (req, res, next) ->
-		{project_id, doc_id} = req.params
-		{fromVersion} = req.query
-		fromVersion = parseInt(fromVersion, 10)
-		MockDocUpdaterServer.getDocument project_id, doc_id, fromVersion, (error, data) ->
-			return next(error) if error?
-			res.json data
+	getDocumentRequest(req, res, next) {
+		const {project_id, doc_id} = req.params;
+		let {fromVersion} = req.query;
+		fromVersion = parseInt(fromVersion, 10);
+		return MockDocUpdaterServer.getDocument(project_id, doc_id, fromVersion, function(error, data) {
+			if (error != null) { return next(error); }
+			return res.json(data);
+		});
+	},
 			
-	deleteProjectRequest: (req, res, next) ->
-		{project_id} = req.params
-		MockDocUpdaterServer.deleteProject project_id, (error) ->
-			return next(error) if error?
-			res.sendStatus 204
+	deleteProjectRequest(req, res, next) {
+		const {project_id} = req.params;
+		return MockDocUpdaterServer.deleteProject(project_id, function(error) {
+			if (error != null) { return next(error); }
+			return res.sendStatus(204);
+		});
+	},
 	
-	running: false
-	run: (callback = (error) ->) ->
-		if MockDocUpdaterServer.running
-			return callback()
-		app = express()
-		app.get "/project/:project_id/doc/:doc_id", MockDocUpdaterServer.getDocumentRequest
-		app.delete "/project/:project_id", MockDocUpdaterServer.deleteProjectRequest
-		app.listen 3003, (error) ->
-			MockDocUpdaterServer.running = true
-			callback(error)
-		.on "error", (error) ->
-			console.error "error starting MockDocUpdaterServer:", error.message
-			process.exit(1)
+	running: false,
+	run(callback) {
+		if (callback == null) { callback = function(error) {}; }
+		if (MockDocUpdaterServer.running) {
+			return callback();
+		}
+		const app = express();
+		app.get("/project/:project_id/doc/:doc_id", MockDocUpdaterServer.getDocumentRequest);
+		app.delete("/project/:project_id", MockDocUpdaterServer.deleteProjectRequest);
+		return app.listen(3003, function(error) {
+			MockDocUpdaterServer.running = true;
+			return callback(error);
+	}).on("error", function(error) {
+			console.error("error starting MockDocUpdaterServer:", error.message);
+			return process.exit(1);
+		});
+	}
+});
 
 			
-sinon.spy MockDocUpdaterServer, "getDocument"
+sinon.spy(MockDocUpdaterServer, "getDocument");

+ 54 - 36
services/real-time/test/acceptance/coffee/helpers/MockWebServer.js

@@ -1,46 +1,64 @@
-sinon = require "sinon"
-express = require "express"
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let MockWebServer;
+const sinon = require("sinon");
+const express = require("express");
 
-module.exports = MockWebServer =
-	projects: {}
-	privileges: {}
+module.exports = (MockWebServer = {
+	projects: {},
+	privileges: {},
 	
-	createMockProject: (project_id, privileges, project) ->
-		MockWebServer.privileges[project_id] = privileges
-		MockWebServer.projects[project_id] = project
+	createMockProject(project_id, privileges, project) {
+		MockWebServer.privileges[project_id] = privileges;
+		return MockWebServer.projects[project_id] = project;
+	},
 		
-	joinProject: (project_id, user_id, callback = (error, project, privilegeLevel) ->) ->
-		callback(
+	joinProject(project_id, user_id, callback) {
+		if (callback == null) { callback = function(error, project, privilegeLevel) {}; }
+		return callback(
 			null,
 			MockWebServer.projects[project_id], 
 			MockWebServer.privileges[project_id][user_id]
-		)
+		);
+	},
 		
-	joinProjectRequest: (req, res, next) ->
-		{project_id} = req.params
-		{user_id} = req.query
-		if project_id == 'rate-limited'
-			res.status(429).send()
-		else
-			MockWebServer.joinProject project_id, user_id, (error, project, privilegeLevel) ->
-				return next(error) if error?
-				res.json {
-					project: project
-					privilegeLevel: privilegeLevel
-				}
+	joinProjectRequest(req, res, next) {
+		const {project_id} = req.params;
+		const {user_id} = req.query;
+		if (project_id === 'rate-limited') {
+			return res.status(429).send();
+		} else {
+			return MockWebServer.joinProject(project_id, user_id, function(error, project, privilegeLevel) {
+				if (error != null) { return next(error); }
+				return res.json({
+					project,
+					privilegeLevel
+				});
+		});
+		}
+	},
 	
-	running: false
-	run: (callback = (error) ->) ->
-		if MockWebServer.running
-			return callback()
-		app = express()
-		app.post "/project/:project_id/join", MockWebServer.joinProjectRequest
-		app.listen 3000, (error) ->
-			MockWebServer.running = true
-			callback(error)
-		.on "error", (error) ->
-			console.error "error starting MockWebServer:", error.message
-			process.exit(1)
+	running: false,
+	run(callback) {
+		if (callback == null) { callback = function(error) {}; }
+		if (MockWebServer.running) {
+			return callback();
+		}
+		const app = express();
+		app.post("/project/:project_id/join", MockWebServer.joinProjectRequest);
+		return app.listen(3000, function(error) {
+			MockWebServer.running = true;
+			return callback(error);
+	}).on("error", function(error) {
+			console.error("error starting MockWebServer:", error.message);
+			return process.exit(1);
+		});
+	}
+});
 
 			
-sinon.spy MockWebServer, "joinProject"
+sinon.spy(MockWebServer, "joinProject");

+ 76 - 57
services/real-time/test/acceptance/coffee/helpers/RealTimeClient.js

@@ -1,75 +1,94 @@
-XMLHttpRequest = require("../../libs/XMLHttpRequest").XMLHttpRequest
-io = require("socket.io-client")
-async = require("async")
+/*
+ * decaffeinate suggestions:
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+let Client;
+const {
+    XMLHttpRequest
+} = require("../../libs/XMLHttpRequest");
+const io = require("socket.io-client");
+const async = require("async");
 
-request = require "request"
-Settings = require "settings-sharelatex"
-redis = require "redis-sharelatex"
-rclient = redis.createClient(Settings.redis.websessions)
+const request = require("request");
+const Settings = require("settings-sharelatex");
+const redis = require("redis-sharelatex");
+const rclient = redis.createClient(Settings.redis.websessions);
 
-uid = require('uid-safe').sync
-signature = require("cookie-signature")
+const uid = require('uid-safe').sync;
+const signature = require("cookie-signature");
 
-io.util.request = () ->
-	xhr = new XMLHttpRequest()
-	_open = xhr.open
-	xhr.open = () =>
-		_open.apply(xhr, arguments)
-		if Client.cookie?
-			xhr.setRequestHeader("Cookie", Client.cookie)
-	return xhr
+io.util.request = function() {
+	const xhr = new XMLHttpRequest();
+	const _open = xhr.open;
+	xhr.open = function() {
+		_open.apply(xhr, arguments);
+		if (Client.cookie != null) {
+			return xhr.setRequestHeader("Cookie", Client.cookie);
+		}
+	}.bind(this);
+	return xhr;
+};
 
-module.exports = Client =
-	cookie: null
+module.exports = (Client = {
+	cookie: null,
 
-	setSession: (session, callback = (error) ->) ->
-		sessionId = uid(24)
-		session.cookie = {}
-		rclient.set "sess:" + sessionId, JSON.stringify(session), (error) ->
-			return callback(error) if error?
-			secret = Settings.security.sessionSecret
-			cookieKey = 's:' + signature.sign(sessionId, secret)
-			Client.cookie = "#{Settings.cookieName}=#{cookieKey}"
-			callback()
+	setSession(session, callback) {
+		if (callback == null) { callback = function(error) {}; }
+		const sessionId = uid(24);
+		session.cookie = {};
+		return rclient.set("sess:" + sessionId, JSON.stringify(session), function(error) {
+			if (error != null) { return callback(error); }
+			const secret = Settings.security.sessionSecret;
+			const cookieKey = 's:' + signature.sign(sessionId, secret);
+			Client.cookie = `${Settings.cookieName}=${cookieKey}`;
+			return callback();
+		});
+	},
 			
-	unsetSession: (callback = (error) ->) ->
-		Client.cookie = null
-		callback()
+	unsetSession(callback) {
+		if (callback == null) { callback = function(error) {}; }
+		Client.cookie = null;
+		return callback();
+	},
 			
-	connect: (cookie) ->
-		client = io.connect("http://localhost:3026", 'force new connection': true)
-		client.on 'connectionAccepted', (_, publicId) ->
-			client.publicId = publicId
-		return client
+	connect(cookie) {
+		const client = io.connect("http://localhost:3026", {'force new connection': true});
+		client.on('connectionAccepted', (_, publicId) => client.publicId = publicId);
+		return client;
+	},
 		
-	getConnectedClients: (callback = (error, clients) ->) ->
-		request.get {
-			url: "http://localhost:3026/clients"
+	getConnectedClients(callback) {
+		if (callback == null) { callback = function(error, clients) {}; }
+		return request.get({
+			url: "http://localhost:3026/clients",
 			json: true
-		}, (error, response, data) ->
-			callback error, data
+		}, (error, response, data) => callback(error, data));
+	},
 		
-	getConnectedClient: (client_id, callback = (error, clients) ->) ->
-		request.get {
-			url: "http://localhost:3026/clients/#{client_id}"
+	getConnectedClient(client_id, callback) {
+		if (callback == null) { callback = function(error, clients) {}; }
+		return request.get({
+			url: `http://localhost:3026/clients/${client_id}`,
 			json: true
-		}, (error, response, data) ->
-			callback error, data
+		}, (error, response, data) => callback(error, data));
+	},
 
 
-	disconnectClient: (client_id, callback) ->
-		request.post {
-			url: "http://localhost:3026/client/#{client_id}/disconnect"
+	disconnectClient(client_id, callback) {
+		request.post({
+			url: `http://localhost:3026/client/${client_id}/disconnect`,
 			auth: {
 				user: Settings.internal.realTime.user,
 				pass: Settings.internal.realTime.pass
 			}
-		}, (error, response, data) ->
-			callback error, data
-		return null
+		}, (error, response, data) => callback(error, data));
+		return null;
+	},
 
-	disconnectAllClients: (callback) ->
-		Client.getConnectedClients (error, clients) ->
-			async.each clients, (clientView, cb) ->
-				Client.disconnectClient clientView.client_id, cb
-			, callback
+	disconnectAllClients(callback) {
+		return Client.getConnectedClients((error, clients) => async.each(clients, (clientView, cb) => Client.disconnectClient(clientView.client_id, cb)
+        , callback));
+	}
+});

+ 44 - 21
services/real-time/test/acceptance/coffee/helpers/RealtimeServer.js

@@ -1,23 +1,46 @@
-app = require('../../../../app')
-logger = require("logger-sharelatex")
-Settings = require("settings-sharelatex")
+/*
+ * decaffeinate suggestions:
+ * DS101: Remove unnecessary use of Array.from
+ * DS102: Remove unnecessary code created because of implicit returns
+ * DS103: Rewrite code to no longer use __guard__
+ * DS205: Consider reworking code to avoid use of IIFEs
+ * DS207: Consider shorter variations of null checks
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
+ */
+const app = require('../../../../app');
+const logger = require("logger-sharelatex");
+const Settings = require("settings-sharelatex");
 
-module.exports =
-	running: false
-	initing: false
-	callbacks: []
-	ensureRunning: (callback = (error) ->) ->
-		if @running
-			return callback()
-		else if @initing
-			@callbacks.push callback
-		else
-			@initing = true
-			@callbacks.push callback
-			app.listen Settings.internal?.realtime?.port, "localhost", (error) => 
-				throw error if error?
-				@running = true
-				logger.log("clsi running in dev mode")
+module.exports = {
+	running: false,
+	initing: false,
+	callbacks: [],
+	ensureRunning(callback) {
+		if (callback == null) { callback = function(error) {}; }
+		if (this.running) {
+			return callback();
+		} else if (this.initing) {
+			return this.callbacks.push(callback);
+		} else {
+			this.initing = true;
+			this.callbacks.push(callback);
+			return app.listen(__guard__(Settings.internal != null ? Settings.internal.realtime : undefined, x => x.port), "localhost", error => { 
+				if (error != null) { throw error; }
+				this.running = true;
+				logger.log("clsi running in dev mode");
 
-				for callback in @callbacks
-					callback()
+				return (() => {
+					const result = [];
+					for (callback of Array.from(this.callbacks)) {
+						result.push(callback());
+					}
+					return result;
+				})();
+			});
+		}
+	}
+};
+
+function __guard__(value, transform) {
+  return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
+}