DiffGeneratorTests.coffee 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. sinon = require('sinon')
  2. chai = require('chai')
  3. should = chai.should()
  4. expect = chai.expect
  5. modulePath = "../../../../app/js/DiffGenerator.js"
  6. SandboxedModule = require('sandboxed-module')
  7. describe "DiffGenerator", ->
  8. beforeEach ->
  9. @DiffGenerator = SandboxedModule.require modulePath
  10. @ts = Date.now()
  11. @user_id = "mock-user-id"
  12. @meta = {
  13. start_ts: @ts, end_ts: @ts, user_id: @user_id
  14. }
  15. describe "rewindUpdate", ->
  16. describe "rewinding an insert", ->
  17. it "should undo the insert", ->
  18. content = "hello world"
  19. update =
  20. op: { p: 6, i: "wo" }
  21. rewoundContent = @DiffGenerator.rewindUpdate content, update
  22. rewoundContent.should.equal "hello rld"
  23. describe "rewinding a delete", ->
  24. it "should undo the delete", ->
  25. content = "hello rld"
  26. update =
  27. op: { p: 6, d: "wo" }
  28. rewoundContent = @DiffGenerator.rewindUpdate content, update
  29. rewoundContent.should.equal "hello world"
  30. describe "with an inconsistent update", ->
  31. it "should throw an error", ->
  32. content = "hello world"
  33. update =
  34. op: { p: 6, i: "foo" }
  35. expect( () =>
  36. @DiffGenerator.rewindUpdate content, update
  37. ).to.throw(@DiffGenerator.ConsistencyError)
  38. describe "rewindUpdates", ->
  39. it "should rewind updates in reverse", ->
  40. content = "aaabbbccc"
  41. updates = [
  42. { op: { p: 3, i: "bbb" } },
  43. { op: { p: 6, i: "ccc" } }
  44. ]
  45. rewoundContent = @DiffGenerator.rewindUpdates content, updates
  46. rewoundContent.should.equal "aaa"
  47. describe "buildDiff", ->
  48. beforeEach ->
  49. @diff = [ u: "mock-diff" ]
  50. @content = "Hello world"
  51. @updates = [
  52. { i: "mock-update-1" }
  53. { i: "mock-update-2" }
  54. { i: "mock-update-3" }
  55. ]
  56. @DiffGenerator.applyUpdateToDiff = sinon.stub().returns(@diff)
  57. @result = @DiffGenerator.buildDiff(@content, @updates)
  58. it "should return the diff", ->
  59. @result.should.deep.equal @diff
  60. it "should build the content into an initial diff", ->
  61. @DiffGenerator.applyUpdateToDiff
  62. .calledWith([{
  63. u: @content
  64. }], @updates[0])
  65. .should.equal true
  66. it "should apply each update", ->
  67. for update in @updates
  68. @DiffGenerator.applyUpdateToDiff
  69. .calledWith(sinon.match.any, update)
  70. .should.equal true
  71. describe "applyUpdateToDiff", ->
  72. describe "an insert", ->
  73. it "should insert into the middle of (u)nchanged text", ->
  74. diff = @DiffGenerator.applyUpdateToDiff(
  75. [ { u: "foobar" } ],
  76. { op: { p: 3, i: "baz" }, meta: @meta }
  77. )
  78. expect(diff).to.deep.equal([
  79. { u: "foo" }
  80. { i: "baz", meta: @meta }
  81. { u: "bar" }
  82. ])
  83. it "should insert into the start of (u)changed text", ->
  84. diff = @DiffGenerator.applyUpdateToDiff(
  85. [ { u: "foobar" } ],
  86. { op: { p: 0, i: "baz" }, meta: @meta }
  87. )
  88. expect(diff).to.deep.equal([
  89. { i: "baz", meta: @meta }
  90. { u: "foobar" }
  91. ])
  92. it "should insert into the end of (u)changed text", ->
  93. diff = @DiffGenerator.applyUpdateToDiff(
  94. [ { u: "foobar" } ],
  95. { op: { p: 6, i: "baz" }, meta: @meta }
  96. )
  97. expect(diff).to.deep.equal([
  98. { u: "foobar" }
  99. { i: "baz", meta: @meta }
  100. ])
  101. it "should insert into the middle of (i)inserted text", ->
  102. diff = @DiffGenerator.applyUpdateToDiff(
  103. [ { i: "foobar", meta: @meta } ],
  104. { op: { p: 3, i: "baz" }, meta: @meta }
  105. )
  106. expect(diff).to.deep.equal([
  107. { i: "foo", meta: @meta }
  108. { i: "baz", meta: @meta }
  109. { i: "bar", meta: @meta }
  110. ])
  111. it "should not count deletes in the running length total", ->
  112. diff = @DiffGenerator.applyUpdateToDiff(
  113. [
  114. { d: "deleted", meta: @meta }
  115. { u: "foobar" }
  116. ],
  117. { op: { p: 3, i: "baz" }, meta: @meta }
  118. )
  119. expect(diff).to.deep.equal([
  120. { d: "deleted", meta: @meta }
  121. { u: "foo" }
  122. { i: "baz", meta: @meta }
  123. { u: "bar" }
  124. ])
  125. describe "a delete", ->
  126. describe "deleting unchanged text", ->
  127. it "should delete from the middle of (u)nchanged text", ->
  128. diff = @DiffGenerator.applyUpdateToDiff(
  129. [ { u: "foobazbar" } ],
  130. { op: { p: 3, d: "baz" }, meta: @meta }
  131. )
  132. expect(diff).to.deep.equal([
  133. { u: "foo" }
  134. { d: "baz", meta: @meta }
  135. { u: "bar" }
  136. ])
  137. it "should delete from the start of (u)nchanged text", ->
  138. diff = @DiffGenerator.applyUpdateToDiff(
  139. [ { u: "foobazbar" } ],
  140. { op: { p: 0, d: "foo" }, meta: @meta }
  141. )
  142. expect(diff).to.deep.equal([
  143. { d: "foo", meta: @meta }
  144. { u: "bazbar" }
  145. ])
  146. it "should delete from the end of (u)nchanged text", ->
  147. diff = @DiffGenerator.applyUpdateToDiff(
  148. [ { u: "foobazbar" } ],
  149. { op: { p: 6, d: "bar" }, meta: @meta }
  150. )
  151. expect(diff).to.deep.equal([
  152. { u: "foobaz" }
  153. { d: "bar", meta: @meta }
  154. ])
  155. it "should delete across multiple (u)changed text parts", ->
  156. diff = @DiffGenerator.applyUpdateToDiff(
  157. [ { u: "foo" }, { u: "baz" }, { u: "bar" } ],
  158. { op: { p: 2, d: "obazb" }, meta: @meta }
  159. )
  160. expect(diff).to.deep.equal([
  161. { u: "fo" }
  162. { d: "o", meta: @meta }
  163. { d: "baz", meta: @meta }
  164. { d: "b", meta: @meta }
  165. { u: "ar" }
  166. ])
  167. describe "deleting inserts", ->
  168. it "should delete from the middle of (i)nserted text", ->
  169. diff = @DiffGenerator.applyUpdateToDiff(
  170. [ { i: "foobazbar", meta: @meta } ],
  171. { op: { p: 3, d: "baz" }, meta: @meta }
  172. )
  173. expect(diff).to.deep.equal([
  174. { i: "foo", meta: @meta }
  175. { i: "bar", meta: @meta }
  176. ])
  177. it "should delete from the start of (u)nchanged text", ->
  178. diff = @DiffGenerator.applyUpdateToDiff(
  179. [ { i: "foobazbar", meta: @meta } ],
  180. { op: { p: 0, d: "foo" }, meta: @meta }
  181. )
  182. expect(diff).to.deep.equal([
  183. { i: "bazbar", meta: @meta }
  184. ])
  185. it "should delete from the end of (u)nchanged text", ->
  186. diff = @DiffGenerator.applyUpdateToDiff(
  187. [ { i: "foobazbar", meta: @meta } ],
  188. { op: { p: 6, d: "bar" }, meta: @meta }
  189. )
  190. expect(diff).to.deep.equal([
  191. { i: "foobaz", meta: @meta }
  192. ])
  193. it "should delete across multiple (u)changed and (i)nserted text parts", ->
  194. diff = @DiffGenerator.applyUpdateToDiff(
  195. [ { u: "foo" }, { i: "baz", meta: @meta }, { u: "bar" } ],
  196. { op: { p: 2, d: "obazb" }, meta: @meta }
  197. )
  198. expect(diff).to.deep.equal([
  199. { u: "fo" }
  200. { d: "o", meta: @meta }
  201. { d: "b", meta: @meta }
  202. { u: "ar" }
  203. ])
  204. describe "deleting over existing deletes", ->
  205. it "should delete across multiple (u)changed and (d)deleted text parts", ->
  206. diff = @DiffGenerator.applyUpdateToDiff(
  207. [ { u: "foo" }, { d: "baz", meta: @meta }, { u: "bar" } ],
  208. { op: { p: 2, d: "ob" }, meta: @meta }
  209. )
  210. expect(diff).to.deep.equal([
  211. { u: "fo" }
  212. { d: "o", meta: @meta }
  213. { d: "baz", meta: @meta }
  214. { d: "b", meta: @meta }
  215. { u: "ar" }
  216. ])
  217. describe "deleting when the text doesn't match", ->
  218. it "should throw an error when deleting from the middle of (u)nchanged text", ->
  219. expect(
  220. () => @DiffGenerator.applyUpdateToDiff(
  221. [ { u: "foobazbar" } ],
  222. { op: { p: 3, d: "xxx" }, meta: @meta }
  223. )
  224. ).to.throw(@DiffGenerator.ConsistencyError)
  225. it "should throw an error when deleting from the start of (u)nchanged text", ->
  226. expect(
  227. () => @DiffGenerator.applyUpdateToDiff(
  228. [ { u: "foobazbar" } ],
  229. { op: { p: 0, d: "xxx" }, meta: @meta }
  230. )
  231. ).to.throw(@DiffGenerator.ConsistencyError)
  232. it "should throw an error when deleting from the end of (u)nchanged text", ->
  233. expect(
  234. () => @DiffGenerator.applyUpdateToDiff(
  235. [ { u: "foobazbar" } ],
  236. { op: { p: 6, d: "xxx" }, meta: @meta }
  237. )
  238. ).to.throw(@DiffGenerator.ConsistencyError)