undo.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. import { Client, expectClients, clearBuffer } from './utils.js';
  2. describe( 'transform', () => {
  3. let john;
  4. beforeEach( () => {
  5. return Client.get( 'john' ).then( client => ( john = client ) );
  6. } );
  7. afterEach( () => {
  8. clearBuffer();
  9. return john.destroy();
  10. } );
  11. it( 'split, remove', () => {
  12. john.setData( '<paragraph>Foo[]Bar</paragraph>' );
  13. john.split();
  14. john.setSelection( [ 1 ], [ 2 ] );
  15. john.remove();
  16. john.undo();
  17. john.undo();
  18. expectClients( '<paragraph>FooBar</paragraph>' );
  19. } );
  20. it( 'move, merge', () => {
  21. john.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph>' );
  22. john.move( [ 2 ] );
  23. john.setSelection( [ 1 ] );
  24. john.merge();
  25. john.undo();
  26. john.undo();
  27. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  28. } );
  29. it.skip( 'move multiple, merge', () => {
  30. john.setData( '[<paragraph>Foo</paragraph><paragraph>Bar</paragraph>]<paragraph>Xyz</paragraph>' );
  31. john.move( [ 3 ] );
  32. expectClients( '<paragraph>Xyz</paragraph><paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  33. john.setSelection( [ 1 ] );
  34. john.merge();
  35. expectClients( '<paragraph>XyzFoo</paragraph><paragraph>Bar</paragraph>' );
  36. john.undo();
  37. expectClients( '<paragraph>Xyz</paragraph><paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  38. john.undo();
  39. // Wrong move is done.
  40. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph><paragraph>Xyz</paragraph>' );
  41. } );
  42. it( 'move inside unwrapped content', () => {
  43. john.setData( '<blockQuote>[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph></blockQuote>' );
  44. john.move( [ 0, 2 ] );
  45. john.setSelection( [ 0, 0 ] );
  46. john.unwrap();
  47. john.undo();
  48. john.undo();
  49. expectClients(
  50. '<blockQuote>' +
  51. '<paragraph>Foo</paragraph>' +
  52. '<paragraph>Bar</paragraph>' +
  53. '</blockQuote>'
  54. );
  55. } );
  56. it( 'remove node, merge', () => {
  57. john.setData( '<paragraph>Foo</paragraph><paragraph>[Bar]</paragraph>' );
  58. john.remove();
  59. john.setSelection( [ 1 ] );
  60. john.merge();
  61. john.undo();
  62. john.undo();
  63. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  64. } );
  65. it( 'merge, merge #1', () => {
  66. john.setData(
  67. '<blockQuote>' +
  68. '<paragraph>Foo</paragraph>' +
  69. '<paragraph>Bar</paragraph>' +
  70. '</blockQuote>' +
  71. '[]' +
  72. '<blockQuote>' +
  73. '<paragraph>Xyz</paragraph>' +
  74. '</blockQuote>'
  75. );
  76. john.merge();
  77. john.setSelection( [ 0, 2 ] );
  78. john.merge();
  79. expectClients(
  80. '<blockQuote>' +
  81. '<paragraph>Foo</paragraph>' +
  82. '<paragraph>BarXyz</paragraph>' +
  83. '</blockQuote>'
  84. );
  85. john.undo();
  86. john.undo();
  87. expectClients(
  88. '<blockQuote>' +
  89. '<paragraph>Foo</paragraph>' +
  90. '<paragraph>Bar</paragraph>' +
  91. '</blockQuote>' +
  92. '<blockQuote>' +
  93. '<paragraph>Xyz</paragraph>' +
  94. '</blockQuote>'
  95. );
  96. } );
  97. it( 'merge, merge #2', () => {
  98. john.setData(
  99. '<blockQuote>' +
  100. '<paragraph>Foo</paragraph>' +
  101. '</blockQuote>' +
  102. '[]' +
  103. '<blockQuote>' +
  104. '<paragraph>Bar</paragraph>' +
  105. '<paragraph>Xyz</paragraph>' +
  106. '</blockQuote>'
  107. );
  108. john.merge();
  109. john.setSelection( [ 0, 1 ] );
  110. john.merge();
  111. expectClients(
  112. '<blockQuote>' +
  113. '<paragraph>FooBar</paragraph>' +
  114. '<paragraph>Xyz</paragraph>' +
  115. '</blockQuote>'
  116. );
  117. john.undo();
  118. john.undo();
  119. expectClients(
  120. '<blockQuote>' +
  121. '<paragraph>Foo</paragraph>' +
  122. '</blockQuote>' +
  123. '<blockQuote>' +
  124. '<paragraph>Bar</paragraph>' +
  125. '<paragraph>Xyz</paragraph>' +
  126. '</blockQuote>'
  127. );
  128. } );
  129. it( 'merge, unwrap', () => {
  130. john.setData( '<paragraph></paragraph>[]<paragraph>Foo</paragraph>' );
  131. john.merge();
  132. john.setSelection( [ 0, 0 ] );
  133. john.unwrap();
  134. john.undo();
  135. john.undo();
  136. expectClients( '<paragraph></paragraph><paragraph>Foo</paragraph>' );
  137. } );
  138. it( 'remove node at the split position #1', () => {
  139. john.setData( '<paragraph>Ab</paragraph>[]<paragraph>Xy</paragraph>' );
  140. john.merge();
  141. john.setSelection( [ 0, 1 ], [ 0, 2 ] );
  142. john.remove();
  143. john.undo();
  144. john.undo();
  145. expectClients( '<paragraph>Ab</paragraph><paragraph>Xy</paragraph>' );
  146. } );
  147. it( 'remove node at the split position #2', () => {
  148. john.setData( '<paragraph>Ab</paragraph>[]<paragraph>Xy</paragraph>' );
  149. john.merge();
  150. john.setSelection( [ 0, 2 ], [ 0, 3 ] );
  151. john.remove();
  152. john.undo();
  153. john.undo();
  154. expectClients( '<paragraph>Ab</paragraph><paragraph>Xy</paragraph>' );
  155. } );
  156. it( 'undoing split after the element created by split has been removed', () => {
  157. // This example is ported here from ckeditor5-undo to keep 100% CC in ckeditor5-engine alone.
  158. john.setData( '<paragraph>Foo[]bar</paragraph>' );
  159. john.split();
  160. john.setSelection( [ 0, 3 ], [ 1, 3 ] );
  161. john.delete();
  162. expectClients( '<paragraph>Foo</paragraph>' );
  163. john.undo();
  164. expectClients( '<paragraph>Foo</paragraph><paragraph>bar</paragraph>' );
  165. john.undo();
  166. expectClients( '<paragraph>Foobar</paragraph>' );
  167. } );
  168. it( 'remove text from paragraph and merge it', () => {
  169. john.setData( '<paragraph>Foo</paragraph><paragraph>[Bar]</paragraph>' );
  170. john.remove();
  171. john.setSelection( [ 1 ] );
  172. john.merge();
  173. expectClients( '<paragraph>Foo</paragraph>' );
  174. john.undo();
  175. expectClients( '<paragraph>Foo</paragraph><paragraph></paragraph>' );
  176. john.undo();
  177. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  178. } );
  179. it( 'delete split paragraphs', () => {
  180. john.setData( '<paragraph>Foo</paragraph><paragraph>B[]ar</paragraph>' );
  181. john.split();
  182. john.setSelection( [ 2, 1 ] );
  183. john.split();
  184. john.setSelection( [ 1, 0 ], [ 3, 1 ] );
  185. john.delete();
  186. john.setSelection( [ 1 ] );
  187. john.merge();
  188. expectClients( '<paragraph>Foo</paragraph>' );
  189. john.undo();
  190. expectClients( '<paragraph>Foo</paragraph><paragraph></paragraph>' );
  191. john.undo();
  192. expectClients( '<paragraph>Foo</paragraph><paragraph>B</paragraph><paragraph>a</paragraph><paragraph>r</paragraph>' );
  193. john.undo();
  194. expectClients( '<paragraph>Foo</paragraph><paragraph>B</paragraph><paragraph>ar</paragraph>' );
  195. john.undo();
  196. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  197. john.redo();
  198. expectClients( '<paragraph>Foo</paragraph><paragraph>B</paragraph><paragraph>ar</paragraph>' );
  199. john.redo();
  200. expectClients( '<paragraph>Foo</paragraph><paragraph>B</paragraph><paragraph>a</paragraph><paragraph>r</paragraph>' );
  201. john.redo();
  202. expectClients( '<paragraph>Foo</paragraph><paragraph></paragraph>' );
  203. john.redo();
  204. expectClients( '<paragraph>Foo</paragraph>' );
  205. } );
  206. it( 'pasting on collapsed selection undo and redo', () => {
  207. john.setData( '<paragraph>Foo[]Bar</paragraph>' );
  208. // Below simulates pasting.
  209. john.editor.model.change( () => {
  210. john.split();
  211. john.setSelection( [ 1 ] );
  212. john.insert( '<paragraph>1</paragraph>' );
  213. john.setSelection( [ 1 ] );
  214. john.merge();
  215. john.setSelection( [ 1 ] );
  216. john.insert( '<paragraph>2</paragraph>' );
  217. john.setSelection( [ 2 ] );
  218. john.merge();
  219. } );
  220. expectClients( '<paragraph>Foo1</paragraph><paragraph>2Bar</paragraph>' );
  221. john.undo();
  222. expectClients( '<paragraph>FooBar</paragraph>' );
  223. john.redo();
  224. expectClients( '<paragraph>Foo1</paragraph><paragraph>2Bar</paragraph>' );
  225. john.undo();
  226. expectClients( '<paragraph>FooBar</paragraph>' );
  227. john.redo();
  228. expectClients( '<paragraph>Foo1</paragraph><paragraph>2Bar</paragraph>' );
  229. } );
  230. it( 'selection attribute setting: split, bold, merge, undo, undo, undo', () => {
  231. // This test is ported from undo to keep 100% CC in engine.
  232. john.setData( '<paragraph>Foo[]</paragraph><paragraph>Bar</paragraph>' );
  233. john.split();
  234. john.setSelection( [ 1, 0 ] );
  235. john._processExecute( 'bold' );
  236. john._processExecute( 'forwardDelete' );
  237. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  238. john.undo();
  239. expectClients( '<paragraph>Foo</paragraph><paragraph selection:bold="true"></paragraph><paragraph>Bar</paragraph>' );
  240. john.undo();
  241. expectClients( '<paragraph>Foo</paragraph><paragraph></paragraph><paragraph>Bar</paragraph>' );
  242. john.undo();
  243. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  244. } );
  245. // https://github.com/ckeditor/ckeditor5/issues/1288
  246. it( 'remove two groups of blocks then undo, undo', () => {
  247. john.setData(
  248. '<paragraph>X</paragraph><paragraph>A</paragraph><paragraph>B[</paragraph><paragraph>C</paragraph><paragraph>D]</paragraph>'
  249. );
  250. john.delete();
  251. john.setSelection( [ 0, 1 ], [ 2, 1 ] );
  252. john.delete();
  253. expectClients( '<paragraph>X</paragraph>' );
  254. john.undo();
  255. expectClients( '<paragraph>X</paragraph><paragraph>A</paragraph><paragraph>B</paragraph>' );
  256. john.undo();
  257. expectClients(
  258. '<paragraph>X</paragraph><paragraph>A</paragraph><paragraph>B</paragraph><paragraph>C</paragraph><paragraph>D</paragraph>'
  259. );
  260. } );
  261. // https://github.com/ckeditor/ckeditor5/issues/1287 TC1
  262. it( 'pasting on non-collapsed selection undo and redo', () => {
  263. john.setData( '<paragraph>Fo[o</paragraph><paragraph>B]ar</paragraph>' );
  264. // Below simulates pasting.
  265. john.editor.model.change( () => {
  266. john.editor.model.deleteContent( john.document.selection );
  267. john.setSelection( [ 0, 2 ] );
  268. john.split();
  269. john.setSelection( [ 1 ] );
  270. john.insert( '<paragraph>1</paragraph>' );
  271. john.setSelection( [ 1 ] );
  272. john.merge();
  273. john.setSelection( [ 1 ] );
  274. john.insert( '<paragraph>2</paragraph>' );
  275. john.setSelection( [ 2 ] );
  276. john.merge();
  277. } );
  278. expectClients( '<paragraph>Fo1</paragraph><paragraph>2ar</paragraph>' );
  279. john.undo();
  280. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  281. john.redo();
  282. expectClients( '<paragraph>Fo1</paragraph><paragraph>2ar</paragraph>' );
  283. john.undo();
  284. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  285. } );
  286. it( 'collapsed marker at the beginning of merged element then undo', () => {
  287. john.setData( '<paragraph>Foo</paragraph><paragraph>[]Bar</paragraph>' );
  288. john.setMarker( 'm1' );
  289. john.setSelection( [ 1 ] );
  290. john.merge();
  291. expectClients( '<paragraph>Foo<m1:start></m1:start>Bar</paragraph>' );
  292. john.undo();
  293. expectClients( '<paragraph>Foo</paragraph><paragraph><m1:start></m1:start>Bar</paragraph>' );
  294. } );
  295. it( 'collapsed marker at the end of merge-target element then undo', () => {
  296. john.setData( '<paragraph>Foo[]</paragraph><paragraph>Bar</paragraph>' );
  297. john.setMarker( 'm1' );
  298. john.setSelection( [ 1 ] );
  299. john.merge();
  300. expectClients( '<paragraph>Foo<m1:start></m1:start>Bar</paragraph>' );
  301. john.undo();
  302. expectClients( '<paragraph>Foo<m1:start></m1:start></paragraph><paragraph>Bar</paragraph>' );
  303. } );
  304. it( 'empty marker between merged elements then undo', () => {
  305. john.setData( '<paragraph>Foo[</paragraph><paragraph>]Bar</paragraph>' );
  306. john.setMarker( 'm1' );
  307. john.setSelection( [ 1 ] );
  308. john.merge();
  309. expectClients( '<paragraph>Foo<m1:start></m1:start>Bar</paragraph>' );
  310. john.undo();
  311. expectClients( '<paragraph>Foo<m1:start></m1:start></paragraph><paragraph><m1:end></m1:end>Bar</paragraph>' );
  312. } );
  313. it( 'left side of marker moved then undo', () => {
  314. john.setData( '<paragraph>Foo[bar]</paragraph><paragraph></paragraph>' );
  315. john.setMarker( 'm1' );
  316. john.setSelection( [ 0, 2 ], [ 0, 4 ] );
  317. john.move( [ 1, 0 ] );
  318. expectClients( '<paragraph>Fo<m1:start></m1:start>ar<m1:end></m1:end></paragraph><paragraph>ob</paragraph>' );
  319. john.undo();
  320. expectClients( '<paragraph>Foo<m1:start></m1:start>bar<m1:end></m1:end></paragraph><paragraph></paragraph>' );
  321. } );
  322. it( 'right side of marker moved then undo', () => {
  323. john.setData( '<paragraph>[Foo]bar</paragraph><paragraph></paragraph>' );
  324. john.setMarker( 'm1' );
  325. john.setSelection( [ 0, 2 ], [ 0, 4 ] );
  326. john.move( [ 1, 0 ] );
  327. expectClients( '<paragraph><m1:start></m1:start>Fo<m1:end></m1:end>ar</paragraph><paragraph>ob</paragraph>' );
  328. john.undo();
  329. expectClients( '<paragraph><m1:start></m1:start>Foo<m1:end></m1:end>bar</paragraph><paragraph></paragraph>' );
  330. } );
  331. } );