wrap.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  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. ] ).then( () => {
  13. john.editor.model.schema.register( 'div', { inheritAllFrom: 'blockQuote' } );
  14. kate.editor.model.schema.register( 'div', { inheritAllFrom: 'blockQuote' } );
  15. } );
  16. } );
  17. afterEach( () => {
  18. clearBuffer();
  19. return Promise.all( [ john.destroy(), kate.destroy() ] );
  20. } );
  21. describe( 'wrap', () => {
  22. describe( 'by wrap', () => {
  23. it( 'element in different path', () => {
  24. john.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph>' );
  25. kate.setData( '<paragraph>Foo</paragraph>[<paragraph>Bar</paragraph>]' );
  26. john.wrap( 'blockQuote' );
  27. kate.wrap( 'div' );
  28. syncClients();
  29. expectClients(
  30. '<blockQuote><paragraph>Foo</paragraph></blockQuote>' +
  31. '<div><paragraph>Bar</paragraph></div>'
  32. );
  33. } );
  34. it( 'the same element', () => {
  35. john.setData( '[<paragraph>Foo</paragraph>]' );
  36. kate.setData( '[<paragraph>Foo</paragraph>]' );
  37. john.wrap( 'blockQuote' );
  38. kate.wrap( 'div' );
  39. syncClients();
  40. // Below would be the expected effect with correct wrap transformation.
  41. // expectClients( '<blockQuote><paragraph>Foo</paragraph></blockQuote>' );
  42. expectClients( '<blockQuote><paragraph>Foo</paragraph></blockQuote><div></div>' );
  43. } );
  44. it( 'intersecting wrap #1', () => {
  45. john.setData( '[<paragraph>Foo</paragraph><paragraph>Bar</paragraph>]<paragraph>Xyz</paragraph>' );
  46. kate.setData( '<paragraph>Foo</paragraph>[<paragraph>Bar</paragraph><paragraph>Xyz</paragraph>]' );
  47. john.wrap( 'blockQuote' );
  48. kate.wrap( 'div' );
  49. syncClients();
  50. // Below would be the expected effect with correct wrap transformation.
  51. // expectClients(
  52. // '<blockQuote>' +
  53. // '<paragraph>Foo</paragraph>' +
  54. // '<paragraph>Bar</paragraph>' +
  55. // '</blockQuote>' +
  56. // '<div>' +
  57. // '<paragraph>Xyz</paragraph>' +
  58. // '</div>'
  59. // );
  60. expectClients(
  61. '<blockQuote><paragraph>Foo</paragraph><div><paragraph>Xyz</paragraph></div><paragraph>Bar</paragraph></blockQuote>'
  62. );
  63. } );
  64. it( 'intersecting wrap #2', () => {
  65. john.setData( '[<paragraph>Foo</paragraph><paragraph>Bar</paragraph><paragraph>Abc</paragraph>]' );
  66. kate.setData( '<paragraph>Foo</paragraph>[<paragraph>Bar</paragraph>]<paragraph>Abc</paragraph>' );
  67. john.wrap( 'blockQuote' );
  68. kate.wrap( 'div' );
  69. syncClients();
  70. // Below would be the expected effect with correct wrap transformation.
  71. // expectClients(
  72. // '<blockQuote>' +
  73. // '<paragraph>Foo</paragraph>' +
  74. // '<paragraph>Bar</paragraph>' +
  75. // '<paragraph>Abc</paragraph>' +
  76. // '</blockQuote>'
  77. // );
  78. expectClients(
  79. '<blockQuote><paragraph>Foo</paragraph><div><paragraph>Bar</paragraph></div><paragraph>Abc</paragraph></blockQuote>'
  80. );
  81. } );
  82. it( 'intersecting wrap #3', () => {
  83. john.setData( '[<paragraph>Foo</paragraph><paragraph>Bar</paragraph>]<paragraph>Abc</paragraph>' );
  84. kate.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph><paragraph>Abc</paragraph>' );
  85. john.wrap( 'blockQuote' );
  86. kate.wrap( 'div' );
  87. syncClients();
  88. // Below would be the expected effect with correct wrap transformation.
  89. // expectClients(
  90. // '<blockQuote>' +
  91. // '<paragraph>Foo</paragraph>' +
  92. // '<paragraph>Bar</paragraph>' +
  93. // '</blockQuote>' +
  94. // '<paragraph>Abc</paragraph>'
  95. // );
  96. expectClients(
  97. '<blockQuote><paragraph>Foo</paragraph><paragraph>Bar</paragraph></blockQuote><div></div><paragraph>Abc</paragraph>'
  98. );
  99. } );
  100. it( 'intersecting wrap, then undo #1', () => {
  101. john.setData( '[<paragraph>Foo</paragraph><paragraph>Bar</paragraph>]<paragraph>Abc</paragraph>' );
  102. kate.setData( '<paragraph>Foo</paragraph>[<paragraph>Bar</paragraph><paragraph>Abc</paragraph>]' );
  103. john.wrap( 'blockQuote' );
  104. kate.wrap( 'div' );
  105. syncClients();
  106. // Below would be the expected effect with correct wrap transformation.
  107. // expectClients(
  108. // '<blockQuote>' +
  109. // '<paragraph>Foo</paragraph>' +
  110. // '<paragraph>Bar</paragraph>' +
  111. // '</blockQuote>' +
  112. // '<div>' +
  113. // '<paragraph>Abc</paragraph>' +
  114. // '</div>'
  115. // );
  116. expectClients(
  117. '<blockQuote><paragraph>Foo</paragraph><div><paragraph>Abc</paragraph></div><paragraph>Bar</paragraph></blockQuote>'
  118. );
  119. john.undo();
  120. kate.undo();
  121. syncClients();
  122. // Below would be the expected effect with correct wrap transformation.
  123. // expectClients(
  124. // '<paragraph>Foo</paragraph>' +
  125. // '<paragraph>Bar</paragraph>' +
  126. // '<paragraph>Abc</paragraph>'
  127. // );
  128. expectClients( '<paragraph>Abc</paragraph><paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  129. } );
  130. it( 'intersecting wrap, then undo #2', () => {
  131. john.setData( '[<paragraph>Foo</paragraph><paragraph>Bar</paragraph>]<paragraph>Abc</paragraph>' );
  132. kate.setData( '<paragraph>Foo</paragraph>[<paragraph>Bar</paragraph><paragraph>Abc</paragraph>]' );
  133. john.wrap( 'blockQuote' );
  134. kate.wrap( 'div' );
  135. syncClients();
  136. // Below would be the expected effect with correct wrap transformation.
  137. // expectClients(
  138. // '<blockQuote>' +
  139. // '<paragraph>Foo</paragraph>' +
  140. // '<paragraph>Bar</paragraph>' +
  141. // '</blockQuote>' +
  142. // '<div>' +
  143. // '<paragraph>Abc</paragraph>' +
  144. // '</div>'
  145. // );
  146. expectClients(
  147. '<blockQuote><paragraph>Foo</paragraph><div><paragraph>Abc</paragraph></div><paragraph>Bar</paragraph></blockQuote>'
  148. );
  149. john.undo();
  150. kate.undo();
  151. syncClients();
  152. // Below would be the expected effect with correct wrap transformation.
  153. // expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph><paragraph>Abc</paragraph>' );
  154. expectClients( '<paragraph>Abc</paragraph><paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  155. } );
  156. it( 'intersecting wrap, then undo #3', () => {
  157. john.setData( '[<paragraph>Foo</paragraph><paragraph>Bar</paragraph>]<paragraph>Abc</paragraph>' );
  158. kate.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph><paragraph>Abc</paragraph>' );
  159. john.wrap( 'blockQuote' );
  160. kate.wrap( 'div' );
  161. syncClients();
  162. john.undo();
  163. kate.undo();
  164. syncClients();
  165. expectClients(
  166. '<paragraph>Foo</paragraph>' +
  167. '<paragraph>Bar</paragraph>' +
  168. '<paragraph>Abc</paragraph>'
  169. );
  170. } );
  171. it( 'element and text', () => {
  172. john.setData( '[<paragraph>Foo</paragraph>]' );
  173. kate.setData( '<paragraph>[Foo]</paragraph>' );
  174. john.wrap( 'blockQuote' );
  175. kate.wrap( 'div' );
  176. syncClients();
  177. expectClients( '<blockQuote><paragraph><div>Foo</div></paragraph></blockQuote>' );
  178. } );
  179. } );
  180. describe( 'by unwrap', () => {
  181. it( 'element in different path', () => {
  182. john.setData( '[<paragraph>Foo</paragraph>]<blockQuote><paragraph>Bar</paragraph></blockQuote>' );
  183. kate.setData( '<paragraph>Foo</paragraph><blockQuote>[<paragraph>Bar</paragraph>]</blockQuote>' );
  184. john.wrap( 'blockQuote' );
  185. kate.unwrap();
  186. syncClients();
  187. expectClients(
  188. '<blockQuote><paragraph>Foo</paragraph></blockQuote>' +
  189. '<paragraph>Bar</paragraph>'
  190. );
  191. } );
  192. it( 'text in different path', () => {
  193. john.setData( '<paragraph>[Foo]</paragraph><blockQuote><paragraph>Bar</paragraph></blockQuote>' );
  194. kate.setData( '<paragraph>Foo</paragraph><blockQuote><paragraph>[]Bar</paragraph></blockQuote>' );
  195. john.wrap( 'div' );
  196. kate.unwrap();
  197. syncClients();
  198. expectClients(
  199. '<paragraph><div>Foo</div></paragraph>' +
  200. '<blockQuote>Bar</blockQuote>'
  201. );
  202. } );
  203. it( 'the same element through undo', () => {
  204. john.setData( '[<paragraph>Foo</paragraph>]' );
  205. kate.setData( '<paragraph>[]Foo</paragraph>' );
  206. john.wrap( 'blockQuote' );
  207. syncClients();
  208. expectClients( '<blockQuote><paragraph>Foo</paragraph></blockQuote>' );
  209. kate.setSelection( [ 0, 0 ] );
  210. kate.unwrap();
  211. syncClients();
  212. expectClients( '<paragraph>Foo</paragraph>' );
  213. john.undo();
  214. kate.undo();
  215. syncClients();
  216. expectClients( '<paragraph>Foo</paragraph>' );
  217. } );
  218. it( 'wrap in unwrapped element', () => {
  219. john.setData( '<blockQuote>[<paragraph>Foo</paragraph>]</blockQuote>' );
  220. kate.setData( '<blockQuote>[]<paragraph>Foo</paragraph></blockQuote>' );
  221. john.wrap( 'div' );
  222. kate.unwrap();
  223. syncClients();
  224. // Below would be the expected effect with correct wrap transformation.
  225. // expectClients( '<div><paragraph>Foo</paragraph></div>' );
  226. expectClients( '<paragraph></paragraph>' );
  227. } );
  228. it.skip( 'wrap in unwrapped element, then undo', () => {
  229. // This is interesting scenario actually. Normally in wrap x wrap situation the stronger wrap just wins
  230. // so we won't get incorrect model. But John was actually to make a wrap like this then he had
  231. // <bq><div><p> structure for a while. So it should be possible to revert to it.
  232. john.setData( '<blockQuote>[<paragraph>Foo</paragraph>]</blockQuote>' );
  233. kate.setData( '<blockQuote>[]<paragraph>Foo</paragraph></blockQuote>' );
  234. john.wrap( 'div' );
  235. kate.unwrap();
  236. syncClients();
  237. expectClients( '<div><paragraph>Foo</paragraph></div>' );
  238. kate.undo();
  239. syncClients();
  240. expectClients( '<blockQuote><div><paragraph>Foo</paragraph></div></blockQuote>' );
  241. } );
  242. it.skip( 'the same text, then undo', () => {
  243. // This is interesting scenario actually. Normally in wrap x wrap situation the stronger wrap just wins
  244. // so we won't get incorrect model. But John was actually to make a wrap like this then he had
  245. // <bq><p><div> structure for a while. So it should be possible to revert to it.
  246. john.setData( '<blockQuote><paragraph>[Foo]</paragraph></blockQuote>' );
  247. kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
  248. john.wrap( 'div' );
  249. kate.unwrap();
  250. syncClients();
  251. expectClients( '<blockQuote><div>Foo</div></blockQuote>' );
  252. kate.undo();
  253. syncClients();
  254. expectClients( '<blockQuote><paragraph><div>Foo</div></paragraph></blockQuote>' );
  255. } );
  256. } );
  257. describe( 'by delete', () => {
  258. it( 'text in two elements #1', () => {
  259. john.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph>' );
  260. kate.setData( '<paragraph>Fo[o</paragraph><paragraph>Ba]r</paragraph>' );
  261. john.wrap( 'blockQuote' );
  262. kate.delete();
  263. syncClients();
  264. expectClients(
  265. '<blockQuote>' +
  266. '<paragraph>For</paragraph>' +
  267. '</blockQuote>'
  268. );
  269. } );
  270. it( 'text in two elements #2', () => {
  271. john.setData( '<paragraph>[Foo]</paragraph><paragraph>Bar</paragraph>' );
  272. kate.setData( '<paragraph>Fo[o</paragraph><paragraph>Ba]r</paragraph>' );
  273. john.wrap( 'div' );
  274. kate.delete();
  275. syncClients();
  276. expectClients( '<paragraph><div>Fo</div>r</paragraph>' );
  277. } );
  278. it( 'delete all wrapped content', () => {
  279. john.setData( '[<paragraph>Foo</paragraph><paragraph>Bar</paragraph><paragraph>Abc</paragraph>]' );
  280. kate.setData( '<paragraph>[Foo</paragraph><paragraph>Bar</paragraph><paragraph>Ab]c</paragraph>' );
  281. john.wrap( 'blockQuote' );
  282. kate.delete();
  283. syncClients();
  284. expectClients( '<blockQuote><paragraph>c</paragraph></blockQuote>' );
  285. } );
  286. it( 'delete all wrapped content and undo', () => {
  287. john.setData( '[<paragraph>Foo</paragraph><paragraph>Bar</paragraph><paragraph>Abc</paragraph>]' );
  288. kate.setData( '<paragraph>[Foo</paragraph><paragraph>Bar</paragraph><paragraph>Ab]c</paragraph>' );
  289. john.wrap( 'blockQuote' );
  290. kate.delete();
  291. syncClients();
  292. expectClients( '<blockQuote><paragraph>c</paragraph></blockQuote>' );
  293. john.undo();
  294. // There is a bug in undo for Kate.
  295. // Kate's content is: '<blockQuote><paragraph>c</paragraph></blockQuote>'.
  296. // Then goes undo and it returns "Foo" paragraph into block quote, but "Bar" goes after it.
  297. // There is a move (reinsert) x wrap transformation and the move is not included inside the wrap.
  298. kate.undo();
  299. syncClients();
  300. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph><paragraph>Abc</paragraph>' );
  301. } );
  302. } );
  303. describe( 'by remove', () => {
  304. it( 'remove the only wrapped element', () => {
  305. john.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph>' );
  306. kate.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph>' );
  307. john.wrap( 'blockQuote' );
  308. kate.remove();
  309. syncClients();
  310. expectClients( '<paragraph>Bar</paragraph>' );
  311. } );
  312. it( 'remove one of two wrapped elements', () => {
  313. john.setData( '[<paragraph>Foo</paragraph><paragraph>Bar</paragraph>]' );
  314. kate.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph>' );
  315. john.wrap( 'blockQuote' );
  316. kate.remove();
  317. syncClients();
  318. expectClients( '<blockQuote><paragraph>Bar</paragraph></blockQuote>' );
  319. } );
  320. it( 'remove all wrapped elements', () => {
  321. john.setData( '[<paragraph>Foo</paragraph><paragraph>Bar</paragraph>]<paragraph>Xyz</paragraph>' );
  322. kate.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph><paragraph>Xyz</paragraph>' );
  323. john.wrap( 'blockQuote' );
  324. kate.remove();
  325. kate.setSelection( [ 0 ], [ 1 ] );
  326. kate.remove();
  327. syncClients();
  328. expectClients( '<paragraph>Xyz</paragraph>' );
  329. } );
  330. it( 'remove the only wrapped element with undo', () => {
  331. john.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph>' );
  332. kate.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph>' );
  333. john.wrap( 'blockQuote' );
  334. kate.remove();
  335. syncClients();
  336. expectClients( '<paragraph>Bar</paragraph>' );
  337. john.undo();
  338. kate.undo();
  339. syncClients();
  340. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  341. } );
  342. it( 'remove one of two wrapped elements with undo', () => {
  343. john.setData( '[<paragraph>Foo</paragraph><paragraph>Bar</paragraph>]' );
  344. kate.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph>' );
  345. john.wrap( 'blockQuote' );
  346. kate.remove();
  347. syncClients();
  348. expectClients( '<blockQuote><paragraph>Bar</paragraph></blockQuote>' );
  349. john.undo();
  350. kate.undo();
  351. syncClients();
  352. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  353. } );
  354. it( 'remove all wrapped elements with undo', () => {
  355. john.setData( '[<paragraph>Foo</paragraph><paragraph>Bar</paragraph>]<paragraph>Xyz</paragraph>' );
  356. kate.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph><paragraph>Xyz</paragraph>' );
  357. john.wrap( 'blockQuote' );
  358. kate.remove();
  359. kate.setSelection( [ 0 ], [ 1 ] );
  360. kate.remove();
  361. syncClients();
  362. expectClients( '<paragraph>Xyz</paragraph>' );
  363. john.undo();
  364. kate.undo();
  365. kate.undo();
  366. syncClients();
  367. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph><paragraph>Xyz</paragraph>' );
  368. } );
  369. } );
  370. describe( 'by merge', () => {
  371. it( 'element into paragraph #1', () => {
  372. john.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph>' );
  373. kate.setData( '<paragraph>Foo</paragraph>[]<paragraph>Bar</paragraph>' );
  374. john.wrap( 'blockQuote' );
  375. kate.merge();
  376. syncClients();
  377. expectClients( '<blockQuote><paragraph>FooBar</paragraph></blockQuote>' );
  378. } );
  379. it( 'element into paragraph #2', () => {
  380. john.setData( '<paragraph>[Foo]</paragraph><paragraph>Bar</paragraph>' );
  381. kate.setData( '<paragraph>Foo</paragraph>[]<paragraph>Bar</paragraph>' );
  382. john.wrap( 'div' );
  383. kate.merge();
  384. syncClients();
  385. john.undo();
  386. kate.undo();
  387. syncClients();
  388. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  389. } );
  390. it( 'element into paragraph, then undo', () => {
  391. john.setData( '<paragraph>Foo</paragraph>[<paragraph>Bar</paragraph>]' );
  392. kate.setData( '<paragraph>Foo</paragraph>[]<paragraph>Bar</paragraph>' );
  393. john.wrap( 'blockQuote' );
  394. kate.merge();
  395. syncClients();
  396. expectClients( '<paragraph>FooBar</paragraph>' );
  397. john.undo();
  398. kate.undo();
  399. syncClients();
  400. expectClients( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>' );
  401. } );
  402. } );
  403. } );
  404. } );