overleaf/services/web/public/coffee/tests/unit/editor/ShareJsDocTests.coffee
2014-02-12 10:23:40 +00:00

251 lines
7.4 KiB
CoffeeScript

define [
"libs/chai"
"editor/ShareJsDoc"
"libs/sharejs"
"libs/sinon"
], (
chai
ShareJsDoc
ShareJs
) ->
should = chai.should()
expect = chai.expect
describe "ShareJsDoc", ->
beforeEach ->
@lines = ["hello", "world"]
@snapshot = @lines.join("\n")
@doc_id = "mock-doc-id"
@version = 42
socket: @socket =
socket:
sessionid: @session_id = "mock-session-id"
connected: true
emit: sinon.stub()
sinon.spy ShareJs.Doc::, "_onMessage"
sinon.spy ShareJs.Doc::, "on"
sinon.spy ShareJs, "Doc"
afterEach ->
ShareJs.Doc::_onMessage.restore()
ShareJs.Doc::on.restore()
ShareJs.Doc.restore()
describe "Creating a ShareJsDoc", ->
beforeEach ->
@shareJsDoc = new ShareJsDoc(@doc_id, @lines, @version, @socket)
it "should create a new ShareJs.Doc instance", ->
expect(@shareJsDoc._doc instanceof ShareJs.Doc).to.be.true
it "should set the ShareJs doc connection", ->
ShareJs.Doc
.calledWith(@shareJsDoc.connection)
.should.equal true
it "should set the ShareJs doc name", ->
ShareJs.Doc
.calledWith(sinon.match.any, @doc_id)
.should.equal true
it "should set the type of the ShareJs doc to 'text'", ->
ShareJs.Doc
.calledWith(sinon.match.any, sinon.match.any, type: "text")
.should.equal true
it "should open the ShareJs doc so that it is in an active state", ->
ShareJs.Doc::_onMessage
.calledWith({
open: true
snapshot: @snapshot
v: @version
})
.should.equal true
it "should bind to the ShareJs doc events", ->
ShareJs.Doc::on.calledWith("change").should.equal true
ShareJs.Doc::on.calledWith("acknowledge").should.equal true
ShareJs.Doc::on.calledWith("remoteop").should.equal true
describe "Sending an op", ->
describe "when the server responds", ->
beforeEach (done) ->
@shareJsDoc = new ShareJsDoc(@doc_id, @lines, @version, @socket)
@shareJsDoc.submitOp @op = [p: 5, i: "foo"], () -> done()
# Send the acknowledgement so that the callback is called
setTimeout () =>
@shareJsDoc.processUpdateFromServer {
v: @version
doc: @doc_id
}
, 10
it "should send the op to the server", ->
@socket.emit
.calledWith("applyOtUpdate", @doc_id, {
doc: @doc_id,
op: @op
v: @version
})
.should.equal true
it "should update the document snapshot", ->
@shareJsDoc.getSnapshot().should.equal "hellofoo\nworld"
describe "when the server does not respond", ->
beforeEach (done) ->
@shareJsDoc = new ShareJsDoc(@doc_id, @lines, @version, @socket)
sinon.spy @shareJsDoc, "trigger"
@shareJsDoc.INFLIGHT_OP_TIMEOUT = 50
@shareJsDoc.submitOp @op = [p: 5, i: "foo"]
setTimeout () ->
done()
, 100
it "should trigger an error", ->
@shareJsDoc.trigger
.calledWith("error", "Doc op was not acknowledged in time")
.should.equal true
describe "clearInflightAndPendingOps", ->
beforeEach ->
@shareJsDoc = new ShareJsDoc(@doc_id, @lines, @version, @socket)
@shareJsDoc._doc.inflightOp = "mock-op-1"
@shareJsDoc._doc.inflightCallbacks = ["mock-callback-1"]
@shareJsDoc._doc.pendingOp = "mock-op-2"
@shareJsDoc._doc.pendingCallbacks = ["mock-callback-2"]
@shareJsDoc.clearInflightAndPendingOps()
it "should clear any inflight or pendings ops", ->
expect(@shareJsDoc._doc.inflightOp).to.be.null
@shareJsDoc._doc.inflightCallbacks.should.deep.equal []
expect(@shareJsDoc._doc.pendingOp).to.be.null
@shareJsDoc._doc.pendingCallbacks.should.deep.equal []
describe "flushPendingOps", ->
beforeEach ->
@shareJsDoc = new ShareJsDoc(@doc_id, @lines, @version, @socket)
@shareJsDoc._doc.flush = sinon.stub()
@shareJsDoc.flushPendingOps()
it "should call _doc.flush", ->
@shareJsDoc._doc.flush.called.should.equal true
describe "updateConnectionState", ->
beforeEach ->
@shareJsDoc = new ShareJsDoc(@doc_id, @lines, @version, @socket)
@shareJsDoc._doc._connectionStateChanged = sinon.stub()
@shareJsDoc._doc.autoOpen = true
@socket.socket.sessionid = "new-connection-id"
@shareJsDoc.updateConnectionState "mock-state"
it "should set autoOpen to false so that ShareJs doesn't try to send an open message", ->
@shareJsDoc._doc.autoOpen.should.equal false
it "should update the connection state", ->
@shareJsDoc.connection.state.should.equal "mock-state"
it "should notify the ShareJs doc of the state change", ->
@shareJsDoc._doc._connectionStateChanged
.calledWith("mock-state")
.should.equal true
it "should set the connection id to the latest socket id", ->
@shareJsDoc.connection.id.should.equal "new-connection-id"
describe "hasBufferedOps", ->
describe "with inflight ops", ->
beforeEach ->
@shareJsDoc = new ShareJsDoc(@doc_id, @lines, @version, @socket)
@shareJsDoc._doc.inflightOp = "mock-op-1"
it "should return true", ->
@shareJsDoc.hasBufferedOps().should.equal true
describe "with pending ops", ->
beforeEach ->
@shareJsDoc = new ShareJsDoc(@doc_id, @lines, @version, @socket)
@shareJsDoc._doc.pendingOp = "mock-op-1"
it "should return true", ->
@shareJsDoc.hasBufferedOps().should.equal true
describe "with no buffered ops", ->
beforeEach ->
@shareJsDoc = new ShareJsDoc(@doc_id, @lines, @version, @socket)
it "should return false", ->
@shareJsDoc.hasBufferedOps().should.equal false
describe "processUpdateFromServer", ->
describe "successfully", ->
beforeEach ->
@shareJsDoc = new ShareJsDoc(@doc_id, @lines, @version, @socket)
@shareJsDoc._doc._onMessage = sinon.stub()
@shareJsDoc.processUpdateFromServer "mock-message"
it "should pass the message onto the ShareJs Doc", ->
@shareJsDoc._doc._onMessage
.calledWith( "mock-message" )
.should.equal true
describe "with an error", ->
beforeEach ->
@shareJsDoc = new ShareJsDoc(@doc_id, @lines, @version, @socket)
sinon.spy @shareJsDoc, "trigger"
@shareJsDoc._doc._onMessage = sinon.stub().throws(@error = {message: "Mock error"})
@shareJsDoc.processUpdateFromServer "mock-message"
it "should trigger the error handler", ->
@shareJsDoc.trigger
.calledWith("error", @error)
.should.equal true
describe "with an external update", ->
beforeEach ->
@shareJsDoc = new ShareJsDoc(@doc_id, @lines, @version, @socket)
@shareJsDoc._doc._onMessage = sinon.stub()
sinon.spy @shareJsDoc, "trigger"
@shareJsDoc.processUpdateFromServer { op: "mock-op", meta: { type: "external" } }
it "should trigger an externalUpdate event", ->
@shareJsDoc.trigger
.calledWith("externalUpdate")
.should.equal true
describe "catchUp", ->
beforeEach ->
@shareJsDoc = new ShareJsDoc(@doc_id, @lines, @version, @socket)
sinon.stub @shareJsDoc, "processUpdateFromServer", () ->
@_doc.version++
@shareJsDoc.catchUp [{
ops: ["mock-op-1"]
}, {
ops: ["mock-op-2"]
}]
it "should apply each update", ->
@shareJsDoc.processUpdateFromServer
.calledWith({
doc: @doc_id
v: @version
ops: ["mock-op-1"]
})
.should.equal true
@shareJsDoc.processUpdateFromServer
.calledWith({
doc: @doc_id
v: @version + 1
ops: ["mock-op-2"]
})
.should.equal true