modifyselection.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* bender-tags: model, composer */
  6. import Document from '/ckeditor5/engine/model/document.js';
  7. import Selection from '/ckeditor5/engine/model/selection.js';
  8. import modifySelection from '/ckeditor5/engine/model/composer/modifyselection.js';
  9. import { setData, stringify } from '/tests/engine/_utils/model.js';
  10. describe( 'Delete utils', () => {
  11. let document;
  12. beforeEach( () => {
  13. document = new Document();
  14. document.schema.registerItem( 'p', '$block' );
  15. document.createRoot();
  16. } );
  17. describe( 'modifySelection', () => {
  18. describe( 'unit=character', () => {
  19. describe( 'within element', () => {
  20. test(
  21. 'does nothing on empty content',
  22. '<selection />',
  23. '<selection />'
  24. );
  25. test(
  26. 'does nothing on empty content (with empty element)',
  27. '<p><selection /></p>',
  28. '<p><selection /></p>'
  29. );
  30. test(
  31. 'does nothing on empty content (backward)',
  32. '<selection />',
  33. '<selection />',
  34. { direction: 'backward' }
  35. );
  36. test(
  37. 'does nothing on root boundary',
  38. '<p>foo<selection /></p>',
  39. '<p>foo<selection /></p>'
  40. );
  41. test(
  42. 'does nothing on root boundary (backward)',
  43. '<p><selection />foo</p>',
  44. '<p><selection />foo</p>',
  45. { direction: 'backward' }
  46. );
  47. test(
  48. 'extends one character forward',
  49. '<p>f<selection />oo</p>',
  50. '<p>f<selection>o</selection>o</p>'
  51. );
  52. test(
  53. 'extends one character backward',
  54. '<p>fo<selection />o</p>',
  55. '<p>f<selection backward>o</selection>o</p>',
  56. { direction: 'backward' }
  57. );
  58. test(
  59. 'extends one character forward (non-collapsed)',
  60. '<p>f<selection>o</selection>obar</p>',
  61. '<p>f<selection>oo</selection>bar</p>'
  62. );
  63. test(
  64. 'extends one character backward (non-collapsed)',
  65. '<p>foob<selection backward>a</selection>r</p>',
  66. '<p>foo<selection backward>ba</selection>r</p>',
  67. { direction: 'backward' }
  68. );
  69. test(
  70. 'extends to element boundary',
  71. '<p>fo<selection />o</p>',
  72. '<p>fo<selection>o</selection></p>'
  73. );
  74. test(
  75. 'extends to element boundary (backward)',
  76. '<p>f<selection />oo</p>',
  77. '<p><selection backward>f</selection>oo</p>',
  78. { direction: 'backward' }
  79. );
  80. test(
  81. 'shrinks forward selection (to collapsed)',
  82. '<p>foo<selection>b</selection>ar</p>',
  83. '<p>foo<selection />bar</p>',
  84. { direction: 'backward' }
  85. );
  86. test(
  87. 'shrinks backward selection (to collapsed)',
  88. '<p>foo<selection backward>b</selection>ar</p>',
  89. '<p>foob<selection />ar</p>'
  90. );
  91. test(
  92. 'extends one element forward',
  93. '<p>f<selection /><img></img>oo</p>',
  94. '<p>f<selection><img></img></selection>oo</p>'
  95. );
  96. test(
  97. 'extends one non-empty element forward',
  98. '<p>f<selection /><img>x</img>oo</p>',
  99. '<p>f<selection><img>x</img></selection>oo</p>'
  100. );
  101. test(
  102. 'extends one element backward',
  103. '<p>fo<img></img><selection />o</p>',
  104. '<p>fo<selection backward><img></img></selection>o</p>',
  105. { direction: 'backward' }
  106. );
  107. //test(
  108. // 'unicode support - forward',
  109. // '<p>நி<selection>லை</selection>க்கு</p>',
  110. // '<p>நி<selection>லைக்</selection>கு</p>'
  111. //);
  112. //
  113. //test(
  114. // 'unicode support - backward',
  115. // '<p>நி<selection backward>லை</selection>க்கு</p>',
  116. // '<p><selection backward>நிலை</selection>க்கு</p>',
  117. // { direction: 'backward' }
  118. //);
  119. test(
  120. 'unicode support - combining mark forward',
  121. '<p>foo<selection />b̂ar</p>',
  122. '<p>foo<selection>b̂</selection>ar</p>'
  123. );
  124. test(
  125. 'unicode support - combining mark backward',
  126. '<p>foob̂<selection />ar</p>',
  127. '<p>foo<selection backward>b̂</selection>ar</p>',
  128. { direction: 'backward' }
  129. );
  130. test(
  131. 'unicode support - combining mark multiple',
  132. '<p>fo<selection />o̻̐ͩbar</p>',
  133. '<p>fo<selection>o̻̐ͩ</selection>bar</p>'
  134. );
  135. test(
  136. 'unicode support - combining mark multiple backward',
  137. '<p>foo̻̐ͩ<selection />bar</p>',
  138. '<p>fo<selection backward>o̻̐ͩ</selection>bar</p>',
  139. { direction: 'backward' }
  140. );
  141. test(
  142. 'unicode support - combining mark to the end',
  143. '<p>fo<selection />o̻̐ͩ</p>',
  144. '<p>fo<selection>o̻̐ͩ</selection></p>'
  145. );
  146. test(
  147. 'unicode support - surrogate pairs forward',
  148. '<p><selection />\uD83D\uDCA9</p>',
  149. '<p><selection>\uD83D\uDCA9</selection></p>'
  150. );
  151. test(
  152. 'unicode support - surrogate pairs backward',
  153. '<p>\uD83D\uDCA9<selection /></p>',
  154. '<p><selection backward>\uD83D\uDCA9</selection></p>',
  155. { direction: 'backward' }
  156. );
  157. } );
  158. describe( 'beyond element', () => {
  159. test(
  160. 'extends over boundary of empty elements',
  161. '<p><selection /></p><p></p><p></p>',
  162. '<p><selection></p><p></selection></p><p></p>'
  163. );
  164. test(
  165. 'extends over boundary of empty elements (backward)',
  166. '<p></p><p></p><p><selection /></p>',
  167. '<p></p><p><selection backward></p><p></selection></p>',
  168. { direction: 'backward' }
  169. );
  170. test(
  171. 'extends over boundary of non-empty elements',
  172. '<p>a<selection /></p><p>bcd</p>',
  173. '<p>a<selection></p><p></selection>bcd</p>'
  174. );
  175. test(
  176. 'extends over boundary of non-empty elements (backward)',
  177. '<p>a</p><p><selection />bcd</p>',
  178. '<p>a<selection backward></p><p></selection>bcd</p>',
  179. { direction: 'backward' }
  180. );
  181. test(
  182. 'extends over character after boundary',
  183. '<p>a<selection></p><p></selection>bcd</p>',
  184. '<p>a<selection></p><p>b</selection>cd</p>'
  185. );
  186. test(
  187. 'extends over character after boundary (backward)',
  188. '<p>abc<selection backward></p><p></selection>d</p>',
  189. '<p>ab<selection backward>c</p><p></selection>d</p>',
  190. { direction: 'backward' }
  191. );
  192. test(
  193. 'extends over boundary when next element has nested elements',
  194. '<p>a<selection /></p><p><x>bcd</x></p>',
  195. '<p>a<selection></p><p></selection><x>bcd</x></p>'
  196. );
  197. test(
  198. 'extends over element when next element has nested elements',
  199. '<p>a<selection></p><p></selection><x>bcd</x>ef</p>',
  200. '<p>a<selection></p><p><x>bcd</x></selection>ef</p>'
  201. );
  202. test(
  203. 'extends over element when next node is a text',
  204. '<p>a<selection /></p>bc',
  205. '<p>a<selection></p></selection>bc'
  206. );
  207. test(
  208. 'extends over element when next node is a text (backward)',
  209. 'ab<p><selection />c</p>',
  210. 'ab<selection backward><p></selection>c</p>',
  211. { direction: 'backward' }
  212. );
  213. test(
  214. 'shrinks over boundary of empty elements',
  215. '<p><selection backward></p><p></selection></p>',
  216. '<p></p><p><selection /></p>'
  217. );
  218. test(
  219. 'shrinks over boundary of empty elements (backward)',
  220. '<p><selection></p><p></selection></p>',
  221. '<p><selection /></p><p></p>',
  222. { direction: 'backward' }
  223. );
  224. test(
  225. 'shrinks over boundary of non-empty elements',
  226. '<p>a<selection backward></p><p></selection>b</p>',
  227. '<p>a</p><p><selection />b</p>'
  228. );
  229. test(
  230. 'shrinks over boundary of non-empty elements (backward)',
  231. '<p>a<selection></p><p></selection>b</p>',
  232. '<p>a<selection /></p><p>b</p>',
  233. { direction: 'backward' }
  234. );
  235. } );
  236. } );
  237. describe( 'unit=codePoint', () => {
  238. test(
  239. 'does nothing on empty content',
  240. '<selection />',
  241. '<selection />',
  242. { unit: 'codePoint' }
  243. );
  244. test(
  245. 'does nothing on empty content (with empty element)',
  246. '<p><selection /></p>',
  247. '<p><selection /></p>',
  248. { unit: 'codePoint' }
  249. );
  250. test(
  251. 'extends one user-perceived character forward - latin letters',
  252. '<p>f<selection />oo</p>',
  253. '<p>f<selection>o</selection>o</p>',
  254. { unit: 'codePoint' }
  255. );
  256. test(
  257. 'extends one user-perceived character backward - latin letters',
  258. '<p>fo<selection />o</p>',
  259. '<p>f<selection backward>o</selection>o</p>',
  260. { unit: 'codePoint', direction: 'backward' }
  261. );
  262. test(
  263. 'unicode support - combining mark forward',
  264. '<p>foo<selection />b̂ar</p>',
  265. '<p>foo<selection>b</selection>̂ar</p>',
  266. { unit: 'codePoint' }
  267. );
  268. test(
  269. 'unicode support - combining mark backward',
  270. '<p>foob̂<selection />ar</p>',
  271. '<p>foob<selection backward>̂</selection>ar</p>',
  272. { unit: 'codePoint', direction: 'backward' }
  273. );
  274. test(
  275. 'unicode support - combining mark multiple',
  276. '<p>fo<selection />o̻̐ͩbar</p>',
  277. '<p>fo<selection>o</selection>̻̐ͩbar</p>',
  278. { unit: 'codePoint' }
  279. );
  280. //test(
  281. // 'extends one unicode code point forward',
  282. // '<p>நி<selection>லை</selection>க்கு</p>',
  283. // '<p>நி<selection>லைக</selection>்கு</p>',
  284. // { unit: 'codePoint' }
  285. //);
  286. //
  287. //test(
  288. // 'shrinks one unicode code point backward (combining mark case) ',
  289. // '<p>நி<selection>லைக்</selection>கு</p>',
  290. // '<p>நி<selection>லைக</selection>்கு</p>',
  291. // { unit: 'codePoint', direction: 'backward' }
  292. //);
  293. test(
  294. 'unicode support - surrogate pairs forward',
  295. '<p><selection />\uD83D\uDCA9</p>',
  296. '<p><selection>\uD83D\uDCA9</selection></p>',
  297. { unit: 'codePoint' }
  298. );
  299. test(
  300. 'unicode support surrogate pairs backward',
  301. '<p>\uD83D\uDCA9<selection /></p>',
  302. '<p><selection backward>\uD83D\uDCA9</selection></p>',
  303. { unit: 'codePoint', direction: 'backward' }
  304. );
  305. } );
  306. } );
  307. function test( title, input, output, options ) {
  308. it( title, () => {
  309. input = input.normalize();
  310. output = output.normalize();
  311. setData( document, input );
  312. // Creating new instance of selection instead of operation on engine.model.Document#selection.
  313. // Document's selection will throw errors in some test cases (which are correct cases, but only for
  314. // non-document selections).
  315. const testSelection = Selection.createFromSelection( document.selection );
  316. modifySelection( testSelection, options );
  317. expect( stringify( document.getRoot(), testSelection ) ).to.equal( output );
  318. } );
  319. }
  320. } );