| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- text = require "../../../../app/js/sharejs/types/text"
- require("chai").should()
- RangesTracker = require "../../../../app/js/RangesTracker"
- describe "ShareJS text type", ->
- beforeEach ->
- @t = "mock-thread-id"
-
- describe "transform", ->
- describe "insert / insert", ->
- it "with an insert before", ->
- dest = []
- text._tc(dest, { i: "foo", p: 9 }, { i: "bar", p: 3 })
- dest.should.deep.equal [{ i: "foo", p: 12 }]
- it "with an insert after", ->
- dest = []
- text._tc(dest, { i: "foo", p: 3 }, { i: "bar", p: 9 })
- dest.should.deep.equal [{ i: "foo", p: 3 }]
- it "with an insert at the same place with side == 'right'", ->
- dest = []
- text._tc(dest, { i: "foo", p: 3 }, { i: "bar", p: 3 }, 'right')
- dest.should.deep.equal [{ i: "foo", p: 6 }]
- it "with an insert at the same place with side == 'left'", ->
- dest = []
- text._tc(dest, { i: "foo", p: 3 }, { i: "bar", p: 3 }, 'left')
- dest.should.deep.equal [{ i: "foo", p: 3 }]
- describe "insert / delete", ->
- it "with a delete before", ->
- dest = []
- text._tc(dest, { i: "foo", p: 9 }, { d: "bar", p: 3 })
- dest.should.deep.equal [{ i: "foo", p: 6 }]
- it "with a delete after", ->
- dest = []
- text._tc(dest, { i: "foo", p: 3 }, { d: "bar", p: 9 })
- dest.should.deep.equal [{ i: "foo", p: 3 }]
- it "with a delete at the same place with side == 'right'", ->
- dest = []
- text._tc(dest, { i: "foo", p: 3 }, { d: "bar", p: 3 }, 'right')
- dest.should.deep.equal [{ i: "foo", p: 3 }]
- it "with a delete at the same place with side == 'left'", ->
- dest = []
-
- text._tc(dest, { i: "foo", p: 3 }, { d: "bar", p: 3 }, 'left')
- dest.should.deep.equal [{ i: "foo", p: 3 }]
- describe "delete / insert", ->
- it "with an insert before", ->
- dest = []
- text._tc(dest, { d: "foo", p: 9 }, { i: "bar", p: 3 })
- dest.should.deep.equal [{ d: "foo", p: 12 }]
- it "with an insert after", ->
- dest = []
- text._tc(dest, { d: "foo", p: 3 }, { i: "bar", p: 9 })
- dest.should.deep.equal [{ d: "foo", p: 3 }]
- it "with an insert at the same place with side == 'right'", ->
- dest = []
- text._tc(dest, { d: "foo", p: 3 }, { i: "bar", p: 3 }, 'right')
- dest.should.deep.equal [{ d: "foo", p: 6 }]
- it "with an insert at the same place with side == 'left'", ->
- dest = []
- text._tc(dest, { d: "foo", p: 3 }, { i: "bar", p: 3 }, 'left')
- dest.should.deep.equal [{ d: "foo", p: 6 }]
-
- it "with a delete that overlaps the insert location", ->
- dest = []
- text._tc(dest, { d: "foo", p: 3 }, { i: "bar", p: 4 })
- dest.should.deep.equal [{ d: "f", p: 3 }, { d: "oo", p: 6 }]
-
- describe "delete / delete", ->
- it "with a delete before", ->
- dest = []
- text._tc(dest, { d: "foo", p: 9 }, { d: "bar", p: 3 })
- dest.should.deep.equal [{ d: "foo", p: 6 }]
- it "with a delete after", ->
- dest = []
- text._tc(dest, { d: "foo", p: 3 }, { d: "bar", p: 9 })
- dest.should.deep.equal [{ d: "foo", p: 3 }]
- it "with deleting the same content", ->
- dest = []
- text._tc(dest, { d: "foo", p: 3 }, { d: "foo", p: 3 }, 'right')
- dest.should.deep.equal []
- it "with the delete overlapping before", ->
- dest = []
- text._tc(dest, { d: "foobar", p: 3 }, { d: "abcfoo", p: 0 }, 'right')
- dest.should.deep.equal [{ d: "bar", p: 0 }]
- it "with the delete overlapping after", ->
- dest = []
- text._tc(dest, { d: "abcfoo", p: 3 }, { d: "foobar", p: 6 })
- dest.should.deep.equal [{ d: "abc", p: 3 }]
- it "with the delete overlapping the whole delete", ->
- dest = []
- text._tc(dest, { d: "abcfoo123", p: 3 }, { d: "foo", p: 6 })
- dest.should.deep.equal [{ d: "abc123", p: 3 }]
- it "with the delete inside the whole delete", ->
- dest = []
- text._tc(dest, { d: "foo", p: 6 }, { d: "abcfoo123", p: 3 })
- dest.should.deep.equal []
-
- describe "comment / insert", ->
- it "with an insert before", ->
- dest = []
- text._tc(dest, { c: "foo", p: 9, @t }, { i: "bar", p: 3 })
- dest.should.deep.equal [{ c: "foo", p: 12, @t }]
- it "with an insert after", ->
- dest = []
- text._tc(dest, { c: "foo", p: 3, @t }, { i: "bar", p: 9 })
- dest.should.deep.equal [{ c: "foo", p: 3, @t }]
- it "with an insert at the left edge", ->
- dest = []
- text._tc(dest, { c: "foo", p: 3, @t }, { i: "bar", p: 3 })
- # RangesTracker doesn't inject inserts into comments on edges, so neither should we
- dest.should.deep.equal [{ c: "foo", p: 6, @t }]
- it "with an insert at the right edge", ->
- dest = []
- text._tc(dest, { c: "foo", p: 3, @t }, { i: "bar", p: 6 })
- # RangesTracker doesn't inject inserts into comments on edges, so neither should we
- dest.should.deep.equal [{ c: "foo", p: 3, @t }]
- it "with an insert in the middle", ->
- dest = []
- text._tc(dest, { c: "foo", p: 3, @t }, { i: "bar", p: 5 })
- dest.should.deep.equal [{ c: "fobaro", p: 3, @t }]
-
- describe "comment / delete", ->
- it "with a delete before", ->
- dest = []
- text._tc(dest, { c: "foo", p: 9, @t }, { d: "bar", p: 3 })
- dest.should.deep.equal [{ c: "foo", p: 6, @t }]
- it "with a delete after", ->
- dest = []
- text._tc(dest, { c: "foo", p: 3, @t }, { i: "bar", p: 9 })
- dest.should.deep.equal [{ c: "foo", p: 3, @t }]
- it "with a delete overlapping the comment content before", ->
- dest = []
- text._tc(dest, { c: "foobar", p: 6, @t }, { d: "123foo", p: 3 })
- dest.should.deep.equal [{ c: "bar", p: 3, @t }]
- it "with a delete overlapping the comment content after", ->
- dest = []
- text._tc(dest, { c: "foobar", p: 6, @t }, { d: "bar123", p: 9 })
- dest.should.deep.equal [{ c: "foo", p: 6, @t }]
- it "with a delete overlapping the comment content in the middle", ->
- dest = []
- text._tc(dest, { c: "foo123bar", p: 6, @t }, { d: "123", p: 9 })
- dest.should.deep.equal [{ c: "foobar", p: 6, @t }]
- it "with a delete overlapping the whole comment", ->
- dest = []
- text._tc(dest, { c: "foo", p: 6, @t }, { d: "123foo456", p: 3 })
- dest.should.deep.equal [{ c: "", p: 3, @t }]
-
- describe "comment / insert", ->
- it "should not do anything", ->
- dest = []
- text._tc(dest, { i: "foo", p: 6 }, { c: "bar", p: 3 })
- dest.should.deep.equal [{ i: "foo", p: 6 }]
-
- describe "comment / delete", ->
- it "should not do anything", ->
- dest = []
- text._tc(dest, { d: "foo", p: 6 }, { c: "bar", p: 3 })
- dest.should.deep.equal [{ d: "foo", p: 6 }]
-
- describe "comment / comment", ->
- it "should not do anything", ->
- dest = []
- text._tc(dest, { c: "foo", p: 6 }, { c: "bar", p: 3 })
- dest.should.deep.equal [{ c: "foo", p: 6 }]
- describe "apply", ->
- it "should apply an insert", ->
- text.apply("foo", [{ i: "bar", p: 2 }]).should.equal "fobaro"
- it "should apply a delete", ->
- text.apply("foo123bar", [{ d: "123", p: 3 }]).should.equal "foobar"
- it "should do nothing with a comment", ->
- text.apply("foo123bar", [{ c: "123", p: 3 }]).should.equal "foo123bar"
-
- it "should throw an error when deleted content does not match", ->
- (() ->
- text.apply("foo123bar", [{ d: "456", p: 3 }])
- ).should.throw(Error)
-
- it "should throw an error when comment content does not match", ->
- (() ->
- text.apply("foo123bar", [{ c: "456", p: 3 }])
- ).should.throw(Error)
-
- describe "applying ops and comments in different orders", ->
- it "should not matter which op or comment is applied first", ->
- transform = (op1, op2, side) ->
- d = []
- text._tc(d, op1, op2, side)
- return d
-
- applySnapshot = (snapshot, op) ->
- return text.apply(snapshot, op)
-
- applyRanges = (rangesTracker, ops) ->
- for op in ops
- rangesTracker.applyOp(op, {})
- return rangesTracker
-
- commentsEqual = (comments1, comments2) ->
- return false if comments1.length != comments2.length
- comments1.sort (a,b) ->
- if a.offset - b.offset == 0
- return a.length - b.length
- else
- return a.offset - b.offset
- comments2.sort (a,b) ->
- if a.offset - b.offset == 0
- return a.length - b.length
- else
- return a.offset - b.offset
- for comment1, i in comments1
- comment2 = comments2[i]
- if comment1.offset != comment2.offset or comment1.length != comment2.length
- return false
- return true
-
- SNAPSHOT = "123"
-
- OPS = []
- # Insert ops
- for p in [0..SNAPSHOT.length]
- OPS.push {i: "a", p: p}
- OPS.push {i: "bc", p: p}
- for p in [0..(SNAPSHOT.length-1)]
- for length in [1..(SNAPSHOT.length - p)]
- OPS.push {d: SNAPSHOT.slice(p, p+length), p}
- for p in [0..(SNAPSHOT.length-1)]
- for length in [1..(SNAPSHOT.length - p)]
- OPS.push {c: SNAPSHOT.slice(p, p+length), p, @t}
- for op1 in OPS
- for op2 in OPS
- op1_t = transform(op1, op2, "left")
- op2_t = transform(op2, op1, "right")
-
- rt12 = new RangesTracker()
- snapshot12 = applySnapshot(applySnapshot(SNAPSHOT, [op1]), op2_t)
- applyRanges(rt12, [op1])
- applyRanges(rt12, op2_t)
-
- rt21 = new RangesTracker()
- snapshot21 = applySnapshot(applySnapshot(SNAPSHOT, [op2]), op1_t)
- applyRanges(rt21, [op2])
- applyRanges(rt21, op1_t)
-
- if snapshot12 != snapshot21
- console.error {op1, op2, op1_t, op2_t, snapshot12, snapshot21}, "Ops are not consistent"
- throw new Error("OT is inconsistent")
-
- if !commentsEqual(rt12.comments, rt21.comments)
- console.log rt12.comments
- console.log rt21.comments
- console.error {op1, op2, op1_t, op2_t, rt12_comments: rt12.comments, rt21_comments: rt21.comments}, "Comments are not consistent"
- throw new Error("OT is inconsistent")
|