UndoManagerTests.coffee 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. define [
  2. "undo/UndoManager"
  3. ], (UndoManager) ->
  4. describe "UndoManager", ->
  5. beforeEach ->
  6. @undoManager = new UndoManager()
  7. describe "Regression Tests", ->
  8. describe "block commenting", ->
  9. it "should convert correctly", ->
  10. lines = ["a", "a", "a", "a"]
  11. simpleDeltas = [
  12. deltas: [
  13. { insert: "%", position: 0 }
  14. { insert: "%", position: 3 }
  15. { insert: "%", position: 6 }
  16. { insert: "%", position: 9 }
  17. ]
  18. group: "doc"
  19. ]
  20. aceDeltas = @undoManager._simpleDeltaSetsToAceDeltaSets(simpleDeltas, lines)
  21. expectedAceDeltas = [
  22. deltas: [{
  23. action: "insertText"
  24. text: "%"
  25. range:
  26. start: column: 0, row: 0
  27. end: column: 1, row: 0
  28. }, {
  29. action: "insertText"
  30. text: "%"
  31. range:
  32. start: column: 0, row: 1
  33. end: column: 1, row: 1
  34. }, {
  35. action: "insertText"
  36. text: "%"
  37. range:
  38. start: column: 0, row: 2
  39. end: column: 1, row: 2
  40. }, {
  41. action: "insertText"
  42. text: "%"
  43. range:
  44. start: column: 0, row: 3
  45. end: column: 1, row: 3
  46. }]
  47. group: "doc"
  48. ]
  49. console.log aceDeltas, expectedAceDeltas
  50. aceDeltas.should.deep.equal expectedAceDeltas
  51. describe "_shiftLocalChangeToTopOfUndoStack", ->
  52. describe "with no local undos", ->
  53. beforeEach ->
  54. @undoManager.undoStack = [
  55. { deltaSets: [], remote: true }
  56. { deltaSets: [], remote: true }
  57. ]
  58. @return = @undoManager._shiftLocalChangeToTopOfUndoStack()
  59. it "should return false", ->
  60. @return.should.equal false
  61. describe "with a local undo that can be shifted to the top", ->
  62. beforeEach ->
  63. @undoManager.undoStack = [
  64. { deltaSets: [deltas: [{ position: 0, insert: "banana"}], group: "doc"], remote: true }
  65. { deltaSets: [deltas: [{ position: 5, insert: "baz" }], group: "doc"], remote: false }
  66. { deltaSets: [deltas: [{ position: 20, insert: "bar" }], group: "doc"], remote: true }
  67. { deltaSets: [deltas: [{ position: 10, insert: "foo" }], group: "doc"], remote: true }
  68. ]
  69. @return = @undoManager._shiftLocalChangeToTopOfUndoStack()
  70. it "should bring the local change to the top of the stack", ->
  71. @expected = [
  72. { deltaSets: [deltas: [{ position: 0, insert: "banana"}], group: "doc"], remote: true }
  73. { deltaSets: [deltas: [{ position: 17, insert: "bar" }], group: "doc"], remote: true }
  74. { deltaSets: [deltas: [{ position: 7, insert: "foo" }], group: "doc"], remote: true }
  75. { deltaSets: [deltas: [{ position: 5, insert: "baz" }], group: "doc"], remote: false }
  76. ]
  77. @undoManager.undoStack.should.deep.equal @expected
  78. it "should return true", ->
  79. @return.should.equal true
  80. describe "with a local undo that cannot be brought all the way to the top", ->
  81. beforeEach ->
  82. @undoManager.undoStack = [
  83. { deltaSets: [deltas: [{ position: 0, insert: "banana"}], group: "doc"], remote: true }
  84. { deltaSets: [deltas: [{ position: 5, insert: "baz" }], group: "doc"], remote: false }
  85. { deltaSets: [deltas: [{ position: 20, insert: "bar" }], group: "doc"], remote: true }
  86. { deltaSets: [deltas: [{ position: 6, insert: "foo" }], group: "doc"], remote: true }
  87. ]
  88. @return = @undoManager._shiftLocalChangeToTopOfUndoStack()
  89. it "should bring the change as far up the stack as possible", ->
  90. @expected = [
  91. { deltaSets: [deltas: [{ position: 0, insert: "banana"}], group: "doc"], remote: true }
  92. { deltaSets: [deltas: [{ position: 17, insert: "bar" }], group: "doc"], remote: true }
  93. { deltaSets: [deltas: [{ position: 5, insert: "baz" }], group: "doc"], remote: false }
  94. { deltaSets: [deltas: [{ position: 6, insert: "foo" }], group: "doc"], remote: true }
  95. ]
  96. @undoManager.undoStack.should.deep.equal @expected
  97. it "should return true", ->
  98. @return.should.equal true
  99. describe "_aceDeltaToSimpleDelta", ->
  100. beforeEach ->
  101. @docLines = [
  102. "one",
  103. "two",
  104. "three",
  105. "four"
  106. ]
  107. describe "insertText", ->
  108. it "should convert correctly", ->
  109. @aceDelta =
  110. action: "insertText"
  111. range:
  112. start:
  113. row: 2
  114. column: 2
  115. end:
  116. row: 2
  117. column: 5
  118. text: "foo"
  119. @simpleDelta = @undoManager._aceDeltaToSimpleDelta(@aceDelta, @docLines)
  120. @simpleDelta.should.deep.equal
  121. insert: "foo"
  122. position: 10
  123. describe "insertLines", ->
  124. it "should convert correctly", ->
  125. @aceDelta =
  126. action: "insertLines"
  127. lines: [
  128. "two and half"
  129. "two and three quarters"
  130. ]
  131. range:
  132. start:
  133. row: 2
  134. column: 0
  135. end:
  136. row: 4
  137. column: 0
  138. @simpleDelta = @undoManager._aceDeltaToSimpleDelta(@aceDelta, @docLines)
  139. @simpleDelta.should.deep.equal
  140. insert: "two and half\ntwo and three quarters\n"
  141. position: 8
  142. describe "removeText", ->
  143. it "should convert correctly", ->
  144. @aceDelta =
  145. action: "removeLines"
  146. range:
  147. start:
  148. row: 1
  149. column: 0
  150. end:
  151. row: 3
  152. column: 0
  153. lines: [
  154. "two",
  155. "three"
  156. ]
  157. @simpleDelta = @undoManager._aceDeltaToSimpleDelta(@aceDelta, @docLines)
  158. @simpleDelta.should.deep.equal
  159. remove: "two\nthree\n"
  160. position: 4
  161. describe "_simpleDeltaToAceDelta", ->
  162. describe "insert", ->
  163. beforeEach ->
  164. @docLines = [
  165. "one",
  166. "two"
  167. "three"
  168. ]
  169. describe "with leading and trailing partial lines", ->
  170. it "should return insertText and insertLines ace updates", ->
  171. @simpleDelta =
  172. position: 7
  173. insert: "after\ntwo and a half\nbefore"
  174. @aceDeltas = @undoManager._simpleDeltaToAceDeltas(@simpleDelta, @docLines)
  175. @expected = [{
  176. text: "after"
  177. range:
  178. start: row: 1, column: 3
  179. end: row:1, column: 8
  180. action: "insertText"
  181. }, {
  182. text: "\n"
  183. range:
  184. start: row: 1, column: 8
  185. end: row:2, column: 0
  186. action: "insertText"
  187. }, {
  188. lines: [ "two and a half" ]
  189. range:
  190. start: row: 2, column:0
  191. end: row:3, column: 0
  192. action: "insertLines"
  193. }, {
  194. text: "before"
  195. range:
  196. start: row: 3, column:0
  197. end: row: 3, column: 6
  198. action: "insertText"
  199. }]
  200. @aceDeltas.should.deep.equal @expected
  201. describe "remove", ->
  202. beforeEach ->
  203. @docLines = [
  204. "one",
  205. "two"
  206. "three"
  207. ]
  208. describe "with leading and trailing partial lines", ->
  209. it "should return insertText and insertLines ace updates", ->
  210. @simpleDelta =
  211. position: 2
  212. remove: "e\ntwo\nth"
  213. @aceDeltas = @undoManager._simpleDeltaToAceDeltas(@simpleDelta, @docLines)
  214. @expected = [{
  215. text: "th"
  216. range:
  217. start: row: 2, column:0
  218. end: row: 2, column: 2
  219. action: "removeText"
  220. }, {
  221. lines: [ "two" ]
  222. range:
  223. start: row: 1, column:0
  224. end: row:2, column: 0
  225. action: "removeLines"
  226. }, {
  227. text: "\n"
  228. range:
  229. start: row: 0, column: 3
  230. end: row:1, column: 0
  231. action: "removeText"
  232. }, {
  233. text: "e"
  234. range:
  235. start: row: 0, column: 2
  236. end: row: 0, column: 3
  237. action: "removeText"
  238. }]
  239. @aceDeltas.should.deep.equal @expected
  240. describe "_concatSimpleDeltas", ->
  241. it "should concat adjacent simple deltas", ->
  242. @result = @undoManager._concatSimpleDeltas [{
  243. insert: "foo"
  244. position: 5
  245. }, {
  246. insert: "bar"
  247. position: 8
  248. }, {
  249. insert: "baz"
  250. position: 11
  251. }, {
  252. insert: "one"
  253. position: 20
  254. }, {
  255. insert: "two"
  256. position: 23
  257. }, {
  258. remove: "three"
  259. position: 26
  260. }]
  261. @result.should.deep.equal [{
  262. insert: "foobarbaz"
  263. position: 5
  264. }, {
  265. insert: "onetwo"
  266. position: 20
  267. }, {
  268. remove: "three"
  269. position: 26
  270. }]
  271. it "should concat removes", ->
  272. @result = @undoManager._concatSimpleDeltas [{
  273. remove: "foo"
  274. position: 5
  275. }, {
  276. remove: "bar"
  277. position: 5
  278. }, {
  279. remove: "baz"
  280. position: 5
  281. }]
  282. @result.should.deep.equal [{
  283. remove: "foobarbaz"
  284. position: 5
  285. }]
  286. describe "_swapSimpleDeltaOrder", ->
  287. describe "insert - insert", ->
  288. describe "when the first delta is before the second", ->
  289. beforeEach ->
  290. # result: "**fooba"
  291. [@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
  292. { insert: "foo", position: 2 }
  293. { insert: "ba", position: 5 }
  294. )
  295. it "should remove the length of the first insert from the second position", ->
  296. @newFirstDelta.should.deep.equal position: 2, insert: "ba"
  297. @newSecondDelta.should.deep.equal position: 2, insert: "foo"
  298. describe "when the second delta is inserted into the first", ->
  299. beforeEach ->
  300. # result "**fobao*"
  301. @result = @undoManager._swapSimpleDeltaOrder(
  302. { insert: "foo", position: 2 }
  303. { insert: "ba", position: 4 }
  304. )
  305. it "should return null", ->
  306. (@result?).should.equal false
  307. describe "when the second delta is before the first", ->
  308. beforeEach ->
  309. # result: "**bafoo"
  310. [@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
  311. { insert: "foo", position: 2 }
  312. { insert: "ba", position: 2 }
  313. )
  314. it "should add the length of the second delta on to the first position", ->
  315. @newFirstDelta.should.deep.equal insert: "ba", position: 2
  316. @newSecondDelta.should.deep.equal insert: "foo", position: 4
  317. describe "remove - remove", ->
  318. describe "when the first delta is before the second", ->
  319. beforeEach ->
  320. # start "**fooba*
  321. [@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
  322. { remove: "foo", position: 2 }
  323. { remove: "ba", position: 2 }
  324. )
  325. it "should add the length of the first remove onto the second position", ->
  326. @newFirstDelta.should.deep.equal position: 5, remove: "ba"
  327. @newSecondDelta.should.deep.equal position: 2, remove: "foo"
  328. describe "when the first and second delta overlap", ->
  329. beforeEach ->
  330. # start "**bfooa*
  331. @result = @undoManager._swapSimpleDeltaOrder(
  332. { remove: "foo", position: 3 }
  333. { remove: "ba", position: 2 }
  334. )
  335. it "should return null", ->
  336. (@result?).should.equal false
  337. describe "when the second delta is before the first", ->
  338. beforeEach ->
  339. # start "**bafoo*
  340. [@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
  341. { remove: "foo", position: 4 }
  342. { remove: "ba", position: 2 }
  343. )
  344. it "should remove the length of the second delta from the first position", ->
  345. @newFirstDelta.should.deep.equal position: 2, remove: "ba"
  346. @newSecondDelta.should.deep.equal position: 2, remove: "foo"
  347. describe "insert - remove", ->
  348. describe "when the first delta is before the second", ->
  349. beforeEach ->
  350. # "**ba" -> "**fooba" -> "**foo"
  351. [@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
  352. { insert: "foo", position: 2 }
  353. { remove: "ba", position: 5 }
  354. )
  355. it "should remove the length of the first delta from the second position", ->
  356. @newFirstDelta.should.deep.equal position: 2, remove: "ba"
  357. @newSecondDelta.should.deep.equal position: 2, insert: "foo"
  358. describe "when the deltas overlap", ->
  359. beforeEach ->
  360. # "**ba" -> "**bfooa" -> "**ooa"
  361. @result = @undoManager._swapSimpleDeltaOrder(
  362. { insert: "foo", position: 3 }
  363. { remove: "bf", position: 2 }
  364. )
  365. it "should return null", ->
  366. (@result?).should.equal false
  367. describe "when the second delta is before the first", ->
  368. beforeEach ->
  369. # "**ba" -> "**bafoo" -> "**foo"
  370. [@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
  371. { insert: "foo", position: 4 }
  372. { remove: "ba", position: 2 }
  373. )
  374. it "should remove the length of the second delta from the first position", ->
  375. @newFirstDelta.should.deep.equal position: 2, remove: "ba"
  376. @newSecondDelta.should.deep.equal position: 2, insert: "foo"
  377. describe "remove - insert", ->
  378. describe "when the first delta is before the second", ->
  379. beforeEach ->
  380. # "**foo" -> "**" -> "**ba"
  381. [@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
  382. { remove: "foo", position: 2 }
  383. { insert: "ba", position: 2 }
  384. )
  385. it "should add the length of the first delta to the second position", ->
  386. @newFirstDelta.should.deep.equal position: 5, insert: "ba"
  387. @newSecondDelta.should.deep.equal position: 2, remove: "foo"
  388. # I don't think the deltas can overlap in this case!
  389. describe "when the first delta is after the second", ->
  390. beforeEach ->
  391. # "**foo" -> "**" -> "*ba*"
  392. [@newFirstDelta, @newSecondDelta] = @undoManager._swapSimpleDeltaOrder(
  393. { remove: "foo", position: 2 }
  394. { insert: "ba", position: 1 }
  395. )
  396. it "should add the length of the second delta on to the first position", ->
  397. @newFirstDelta.should.deep.equal position: 1, insert: "ba"
  398. @newSecondDelta.should.deep.equal position: 4, remove: "foo"