merge.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. import { Client, syncClients, expectClients, clearBuffer } from './utils.js';
  6. describe( 'transform', () => {
  7. let john, kate;
  8. beforeEach( () => {
  9. return Promise.all( [
  10. Client.get( 'john' ).then( client => ( john = client ) ),
  11. Client.get( 'kate' ).then( client => ( kate = client ) )
  12. ] );
  13. } );
  14. afterEach( () => {
  15. clearBuffer();
  16. return Promise.all( [ john.destroy(), kate.destroy() ] );
  17. } );
  18. describe( 'merge', () => {
  19. describe( 'by merge', () => {
  20. it( 'elements into paragraph', () => {
  21. john.setData( '<paragraph>Foo</paragraph>[]<paragraph>Bar</paragraph><paragraph>Abc</paragraph>' );
  22. kate.setData( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>[]<paragraph>Abc</paragraph>' );
  23. john.merge();
  24. kate.merge();
  25. syncClients();
  26. expectClients( '<paragraph>FooBarAbc</paragraph>' );
  27. } );
  28. it( 'elements into paragraph with undo', () => {
  29. john.setData( '<paragraph>Foo</paragraph>[]<paragraph>Bar</paragraph><paragraph>Abc</paragraph>' );
  30. kate.setData( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>[]<paragraph>Abc</paragraph>' );
  31. john.merge();
  32. kate.merge();
  33. syncClients();
  34. kate.undo();
  35. john.undo();
  36. syncClients();
  37. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph><paragraph>Abc</paragraph>' );
  38. } );
  39. it.skip( 'same element with undo', () => {
  40. // Unnecessary SplitOperation.
  41. john.setData( '<paragraph>Foo</paragraph>[]<paragraph></paragraph>' );
  42. kate.setData( '<paragraph>Foo</paragraph>[]<paragraph></paragraph>' );
  43. john.merge();
  44. kate.merge();
  45. kate.undo();
  46. syncClients();
  47. expectClients( '<paragraph>Foo</paragraph>' );
  48. } );
  49. it( 'merge to different element (insert) with undo', () => {
  50. john.setData( '<paragraph>Foo</paragraph>[]<paragraph>Bar</paragraph>' );
  51. kate.setData( '<paragraph>Foo</paragraph>[]<paragraph>Bar</paragraph>' );
  52. john.merge();
  53. kate.insert( '<paragraph></paragraph>' );
  54. kate.setSelection( [ 2 ], [ 2 ] );
  55. kate.merge();
  56. syncClients();
  57. expectClients( '<paragraph>FooBar</paragraph><paragraph></paragraph>' );
  58. john.undo();
  59. syncClients();
  60. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph><paragraph></paragraph>' );
  61. kate.undo();
  62. syncClients();
  63. expectClients( '<paragraph>FooBar</paragraph><paragraph></paragraph><paragraph></paragraph>' );
  64. } );
  65. it( 'merge to different element (wrap) with undo #1', () => {
  66. // Merging to an element happens during using BlockQuote plugin. `Foo` paragraph is first wrapped and
  67. // then merged to the existing `blockQuote`.
  68. john.setData( '<paragraph>F[oo</paragraph><blockQuote><paragraph>B]ar</paragraph></blockQuote>' );
  69. kate.setData( '<paragraph>F[oo</paragraph><blockQuote><paragraph>B]ar</paragraph></blockQuote>' );
  70. john._processExecute( 'blockQuote' );
  71. kate._processExecute( 'blockQuote' );
  72. syncClients();
  73. expectClients(
  74. '<blockQuote>' +
  75. '<paragraph>Foo</paragraph>' +
  76. '<paragraph>Bar</paragraph>' +
  77. '</blockQuote>'
  78. );
  79. john.undo();
  80. syncClients();
  81. expectClients(
  82. '<paragraph>Foo</paragraph>' +
  83. '<blockQuote>' +
  84. '<paragraph>Bar</paragraph>' +
  85. '</blockQuote>'
  86. );
  87. kate.undo();
  88. syncClients();
  89. expectClients(
  90. '<paragraph>Foo</paragraph>' +
  91. '<blockQuote>' +
  92. '<paragraph>Bar</paragraph>' +
  93. '</blockQuote>'
  94. );
  95. } );
  96. it( 'merge to different element (wrap) with undo #2', () => {
  97. // Merging to an element happens during using BlockQuote plugin. `Foo` paragraph is first wrapped and
  98. // then merged to the existing `blockQuote`.
  99. john.setData( '<paragraph>F[oo</paragraph><blockQuote><paragraph>B]ar</paragraph></blockQuote>' );
  100. kate.setData( '<paragraph>F[oo</paragraph><blockQuote><paragraph>B]ar</paragraph></blockQuote>' );
  101. john._processExecute( 'blockQuote' );
  102. kate._processExecute( 'blockQuote' );
  103. syncClients();
  104. expectClients(
  105. '<blockQuote>' +
  106. '<paragraph>Foo</paragraph>' +
  107. '<paragraph>Bar</paragraph>' +
  108. '</blockQuote>'
  109. );
  110. kate.undo();
  111. syncClients();
  112. expectClients(
  113. '<blockQuote>' +
  114. '<paragraph>Bar</paragraph>' +
  115. '</blockQuote>' +
  116. '<paragraph>Foo</paragraph>'
  117. );
  118. john.undo();
  119. syncClients();
  120. expectClients(
  121. '<paragraph>Foo</paragraph>'
  122. );
  123. } );
  124. it( 'merge to different element (wrap) with undo #3', () => {
  125. // Merging to an element happens during using BlockQuote plugin. `Foo` paragraph is first wrapped and
  126. // then merged to the existing `blockQuote`.
  127. john.setData( '<paragraph>F[oo</paragraph><blockQuote><paragraph>B]ar</paragraph></blockQuote>' );
  128. kate.setData( '<paragraph>F[oo</paragraph><blockQuote><paragraph>B]ar</paragraph></blockQuote>' );
  129. john._processExecute( 'blockQuote' );
  130. kate._processExecute( 'blockQuote' );
  131. syncClients();
  132. expectClients(
  133. '<blockQuote>' +
  134. '<paragraph>Foo</paragraph>' +
  135. '<paragraph>Bar</paragraph>' +
  136. '</blockQuote>'
  137. );
  138. john.undo();
  139. kate.undo();
  140. syncClients();
  141. expectClients( '<paragraph>Foo</paragraph>' );
  142. } );
  143. } );
  144. describe( 'by remove', () => {
  145. it( 'remove merged element', () => {
  146. john.setData( '<paragraph>Foo</paragraph>[]<paragraph>Bar</paragraph>' );
  147. kate.setData( '<paragraph>Foo</paragraph>[<paragraph>Bar</paragraph>]' );
  148. john.merge();
  149. kate.remove();
  150. syncClients();
  151. expectClients( '<paragraph>Foo</paragraph>' );
  152. } );
  153. it( 'remove merged element then undo #1', () => {
  154. john.setData( '<paragraph>Foo</paragraph>[]<paragraph>Bar</paragraph>' );
  155. kate.setData( '<paragraph>Foo</paragraph>[<paragraph>Bar</paragraph>]' );
  156. john.merge();
  157. kate.remove();
  158. syncClients();
  159. expectClients( '<paragraph>Foo</paragraph>' );
  160. kate.undo();
  161. syncClients();
  162. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  163. } );
  164. it( 'remove merged element then undo #2', () => {
  165. john.setData( '<paragraph>Foo</paragraph>[]<paragraph>Bar</paragraph>' );
  166. kate.setData( '<paragraph>Foo</paragraph>[<paragraph>Bar</paragraph>]' );
  167. john.merge();
  168. kate.remove();
  169. kate.undo();
  170. syncClients();
  171. expectClients( '<paragraph>FooBar</paragraph>' );
  172. } );
  173. it( 'remove merged element then undo #3', () => {
  174. john.setData( '[<paragraph>A</paragraph><paragraph>B</paragraph>]<paragraph>C</paragraph>' );
  175. kate.setData( '<paragraph>A</paragraph>[]<paragraph>B</paragraph><paragraph>C</paragraph>' );
  176. kate.merge();
  177. john.remove();
  178. syncClients();
  179. expectClients( '<paragraph>C</paragraph>' );
  180. john.undo();
  181. kate.undo();
  182. syncClients();
  183. expectClients( '<paragraph>A</paragraph><paragraph>B</paragraph><paragraph>C</paragraph>' );
  184. } );
  185. it( 'remove merged element then undo #4', () => {
  186. john.setData( '<paragraph>A</paragraph>[<paragraph>B</paragraph><paragraph>C</paragraph>]' );
  187. kate.setData( '<paragraph>A</paragraph>[]<paragraph>B</paragraph><paragraph>C</paragraph>' );
  188. kate.merge();
  189. john.remove();
  190. syncClients();
  191. expectClients( '<paragraph>A</paragraph>' );
  192. john.undo();
  193. kate.undo();
  194. syncClients();
  195. expectClients( '<paragraph>A</paragraph><paragraph>B</paragraph><paragraph>C</paragraph>' );
  196. } );
  197. } );
  198. describe( 'by delete', () => {
  199. it( 'text from two elements', () => {
  200. john.setData( '<paragraph>Foo</paragraph>[]<paragraph>Bar</paragraph>' );
  201. kate.setData( '<paragraph>Fo[o</paragraph><paragraph>Ba]r</paragraph>' );
  202. john.merge();
  203. kate.delete();
  204. syncClients();
  205. expectClients( '<paragraph>For</paragraph>' );
  206. } );
  207. it( 'merged elements and some text', () => {
  208. john.setData( '<paragraph>Foo</paragraph><paragraph></paragraph>[]<paragraph>Bar</paragraph>' );
  209. kate.setData( '<paragraph>F[oo</paragraph><paragraph></paragraph><paragraph>Ba]r</paragraph>' );
  210. john.merge();
  211. kate.delete();
  212. syncClients();
  213. expectClients( '<paragraph>Fr</paragraph>' );
  214. } );
  215. } );
  216. describe( 'by wrap', () => {
  217. it( 'wrap merged element', () => {
  218. john.setData( '<paragraph>Foo</paragraph>[]<paragraph>Bar</paragraph>' );
  219. kate.setData( '<paragraph>Foo</paragraph>[<paragraph>Bar</paragraph>]' );
  220. john.merge();
  221. kate.wrap( 'blockQuote' );
  222. syncClients();
  223. expectClients( '<paragraph>FooBar</paragraph>' );
  224. john.undo();
  225. kate.undo();
  226. syncClients();
  227. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  228. } );
  229. it( 'wrap in merged element', () => {
  230. // This is pretty weird case. Right now it cannot be reproduced with the features that we have.
  231. john.editor.model.schema.extend( 'paragraph', { allowIn: 'listItem' } );
  232. john.editor.model.schema.extend( 'blockQuote', { allowIn: 'listItem' } );
  233. kate.editor.model.schema.extend( 'paragraph', { allowIn: 'listItem' } );
  234. kate.editor.model.schema.extend( 'blockQuote', { allowIn: 'listItem' } );
  235. john.setData(
  236. '<listItem>' +
  237. '<paragraph>A</paragraph>' +
  238. '</listItem>' +
  239. '[]' +
  240. '<listItem>' +
  241. '<paragraph>B</paragraph>' +
  242. '<paragraph>C</paragraph>' +
  243. '</listItem>'
  244. );
  245. kate.setData(
  246. '<listItem>' +
  247. '<paragraph>A</paragraph>' +
  248. '</listItem>' +
  249. '<listItem>' +
  250. '[<paragraph>B</paragraph>' +
  251. '<paragraph>C</paragraph>]' +
  252. '</listItem>'
  253. );
  254. john.merge();
  255. kate.wrap( 'blockQuote' );
  256. syncClients();
  257. expectClients(
  258. '<listItem>' +
  259. '<paragraph>A</paragraph>' +
  260. '<blockQuote>' +
  261. '<paragraph>B</paragraph>' +
  262. '<paragraph>C</paragraph>' +
  263. '</blockQuote>' +
  264. '</listItem>'
  265. );
  266. } );
  267. } );
  268. describe( 'by unwrap', () => {
  269. it( 'merge to unwrapped element', () => {
  270. john.setData( '<blockQuote><paragraph>Foo</paragraph></blockQuote>[]<blockQuote><paragraph>Bar</paragraph></blockQuote>' );
  271. kate.setData( '<blockQuote>[]<paragraph>Foo</paragraph></blockQuote><blockQuote><paragraph>Bar</paragraph></blockQuote>' );
  272. john.merge();
  273. kate.unwrap();
  274. syncClients();
  275. // Below would be the expected effect with correct wrap transformation.
  276. // expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  277. expectClients( '<paragraph>Foo</paragraph>' );
  278. } );
  279. it( 'merge to unwrapped element with undo #1', () => {
  280. john.setData( '<blockQuote><paragraph>Foo</paragraph></blockQuote>[]<blockQuote><paragraph>Bar</paragraph></blockQuote>' );
  281. kate.setData( '<blockQuote>[]<paragraph>Foo</paragraph></blockQuote><blockQuote><paragraph>Bar</paragraph></blockQuote>' );
  282. john.merge();
  283. john.undo();
  284. kate.unwrap();
  285. syncClients();
  286. // Below would be the expected effect with correct wrap transformation.
  287. // expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  288. expectClients( '<paragraph>Foo</paragraph><blockQuote><paragraph>Bar</paragraph></blockQuote>' );
  289. } );
  290. it( 'merge to unwrapped element with undo #2', () => {
  291. john.setData( '<blockQuote><paragraph>Foo</paragraph></blockQuote>[]<blockQuote><paragraph>Bar</paragraph></blockQuote>' );
  292. kate.setData( '<blockQuote>[]<paragraph>Foo</paragraph></blockQuote><blockQuote><paragraph>Bar</paragraph></blockQuote>' );
  293. john.merge();
  294. kate.unwrap();
  295. kate.undo();
  296. syncClients();
  297. // Below would be the expected effect with correct wrap transformation.
  298. // expectClients( '<blockQuote><paragraph>Foo</paragraph></blockQuote><paragraph>Bar</paragraph>' );
  299. expectClients( '<blockQuote><paragraph>Foo</paragraph><paragraph>Bar</paragraph></blockQuote>' );
  300. } );
  301. it( 'unwrap in merged element', () => {
  302. // This is pretty weird case. Right now it cannot be reproduced with the features that we have.
  303. john.editor.model.schema.extend( 'paragraph', { allowIn: 'listItem' } );
  304. john.editor.model.schema.extend( 'blockQuote', { allowIn: 'listItem' } );
  305. kate.editor.model.schema.extend( 'paragraph', { allowIn: 'listItem' } );
  306. kate.editor.model.schema.extend( 'blockQuote', { allowIn: 'listItem' } );
  307. john.setData(
  308. '<listItem>' +
  309. '<paragraph>A</paragraph>' +
  310. '</listItem>' +
  311. '[]' +
  312. '<listItem>' +
  313. '<blockQuote>' +
  314. '<paragraph>B</paragraph>' +
  315. '<paragraph>C</paragraph>' +
  316. '</blockQuote>' +
  317. '</listItem>'
  318. );
  319. kate.setData(
  320. '<listItem>' +
  321. '<paragraph>A</paragraph>' +
  322. '</listItem>' +
  323. '<listItem>' +
  324. '<blockQuote>' +
  325. '[]<paragraph>B</paragraph>' +
  326. '<paragraph>C</paragraph>' +
  327. '</blockQuote>' +
  328. '</listItem>'
  329. );
  330. john.merge();
  331. kate.unwrap();
  332. syncClients();
  333. expectClients(
  334. '<listItem>' +
  335. '<paragraph>A</paragraph>' +
  336. '<paragraph>B</paragraph>' +
  337. '<paragraph>C</paragraph>' +
  338. '</listItem>'
  339. );
  340. } );
  341. } );
  342. describe( 'by split', () => {
  343. it( 'merge element which got split (the element is in blockquote) and undo', () => {
  344. john.setData( '<paragraph>Foo</paragraph><blockQuote><paragraph>[]Bar</paragraph></blockQuote>' );
  345. kate.setData( '<paragraph>Foo</paragraph><blockQuote><paragraph>B[]ar</paragraph></blockQuote>' );
  346. john._processExecute( 'delete' );
  347. kate.split();
  348. syncClients();
  349. expectClients( '<paragraph>FooB</paragraph><paragraph>ar</paragraph>' );
  350. john.undo();
  351. syncClients();
  352. expectClients( '<paragraph>Foo</paragraph><blockQuote><paragraph>B</paragraph><paragraph>ar</paragraph></blockQuote>' );
  353. } );
  354. } );
  355. } );
  356. } );