8
0

modifyselection.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  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 - combining mark forward',
  109. '<p>foo<selection />b̂ar</p>',
  110. '<p>foo<selection>b̂</selection>ar</p>'
  111. );
  112. test(
  113. 'unicode support - combining mark backward',
  114. '<p>foob̂<selection />ar</p>',
  115. '<p>foo<selection backward>b̂</selection>ar</p>',
  116. { direction: 'backward' }
  117. );
  118. test(
  119. 'unicode support - combining mark multiple',
  120. '<p>fo<selection />o̻̐ͩbar</p>',
  121. '<p>fo<selection>o̻̐ͩ</selection>bar</p>'
  122. );
  123. test(
  124. 'unicode support - combining mark multiple backward',
  125. '<p>foo̻̐ͩ<selection />bar</p>',
  126. '<p>fo<selection backward>o̻̐ͩ</selection>bar</p>',
  127. { direction: 'backward' }
  128. );
  129. test(
  130. 'unicode support - combining mark to the end',
  131. '<p>fo<selection />o̻̐ͩ</p>',
  132. '<p>fo<selection>o̻̐ͩ</selection></p>'
  133. );
  134. test(
  135. 'unicode support - surrogate pairs forward',
  136. '<p><selection />\uD83D\uDCA9</p>',
  137. '<p><selection>\uD83D\uDCA9</selection></p>'
  138. );
  139. test(
  140. 'unicode support - surrogate pairs backward',
  141. '<p>\uD83D\uDCA9<selection /></p>',
  142. '<p><selection backward>\uD83D\uDCA9</selection></p>',
  143. { direction: 'backward' }
  144. );
  145. } );
  146. describe( 'beyond element', () => {
  147. test(
  148. 'extends over boundary of empty elements',
  149. '<p><selection /></p><p></p><p></p>',
  150. '<p><selection></p><p></selection></p><p></p>'
  151. );
  152. test(
  153. 'extends over boundary of empty elements (backward)',
  154. '<p></p><p></p><p><selection /></p>',
  155. '<p></p><p><selection backward></p><p></selection></p>',
  156. { direction: 'backward' }
  157. );
  158. test(
  159. 'extends over boundary of non-empty elements',
  160. '<p>a<selection /></p><p>bcd</p>',
  161. '<p>a<selection></p><p></selection>bcd</p>'
  162. );
  163. test(
  164. 'extends over boundary of non-empty elements (backward)',
  165. '<p>a</p><p><selection />bcd</p>',
  166. '<p>a<selection backward></p><p></selection>bcd</p>',
  167. { direction: 'backward' }
  168. );
  169. test(
  170. 'extends over character after boundary',
  171. '<p>a<selection></p><p></selection>bcd</p>',
  172. '<p>a<selection></p><p>b</selection>cd</p>'
  173. );
  174. test(
  175. 'extends over character after boundary (backward)',
  176. '<p>abc<selection backward></p><p></selection>d</p>',
  177. '<p>ab<selection backward>c</p><p></selection>d</p>',
  178. { direction: 'backward' }
  179. );
  180. test(
  181. 'extends over boundary when next element has nested elements',
  182. '<p>a<selection /></p><p><x>bcd</x></p>',
  183. '<p>a<selection></p><p></selection><x>bcd</x></p>'
  184. );
  185. test(
  186. 'extends over element when next element has nested elements',
  187. '<p>a<selection></p><p></selection><x>bcd</x>ef</p>',
  188. '<p>a<selection></p><p><x>bcd</x></selection>ef</p>'
  189. );
  190. test(
  191. 'extends over element when next node is a text',
  192. '<p>a<selection /></p>bc',
  193. '<p>a<selection></p></selection>bc'
  194. );
  195. test(
  196. 'extends over element when next node is a text (backward)',
  197. 'ab<p><selection />c</p>',
  198. 'ab<selection backward><p></selection>c</p>',
  199. { direction: 'backward' }
  200. );
  201. test(
  202. 'shrinks over boundary of empty elements',
  203. '<p><selection backward></p><p></selection></p>',
  204. '<p></p><p><selection /></p>'
  205. );
  206. test(
  207. 'shrinks over boundary of empty elements (backward)',
  208. '<p><selection></p><p></selection></p>',
  209. '<p><selection /></p><p></p>',
  210. { direction: 'backward' }
  211. );
  212. test(
  213. 'shrinks over boundary of non-empty elements',
  214. '<p>a<selection backward></p><p></selection>b</p>',
  215. '<p>a</p><p><selection />b</p>'
  216. );
  217. test(
  218. 'shrinks over boundary of non-empty elements (backward)',
  219. '<p>a<selection></p><p></selection>b</p>',
  220. '<p>a<selection /></p><p>b</p>',
  221. { direction: 'backward' }
  222. );
  223. } );
  224. } );
  225. describe( 'unit=codePoint', () => {
  226. test(
  227. 'does nothing on empty content',
  228. '<selection />',
  229. '<selection />',
  230. { unit: 'codePoint' }
  231. );
  232. test(
  233. 'does nothing on empty content (with empty element)',
  234. '<p><selection /></p>',
  235. '<p><selection /></p>',
  236. { unit: 'codePoint' }
  237. );
  238. test(
  239. 'extends one user-perceived character forward - latin letters',
  240. '<p>f<selection />oo</p>',
  241. '<p>f<selection>o</selection>o</p>',
  242. { unit: 'codePoint' }
  243. );
  244. test(
  245. 'extends one user-perceived character backward - latin letters',
  246. '<p>fo<selection />o</p>',
  247. '<p>f<selection backward>o</selection>o</p>',
  248. { unit: 'codePoint', direction: 'backward' }
  249. );
  250. test(
  251. 'unicode support - combining mark forward',
  252. '<p>foo<selection />b̂ar</p>',
  253. '<p>foo<selection>b</selection>̂ar</p>',
  254. { unit: 'codePoint' }
  255. );
  256. test(
  257. 'unicode support - combining mark backward',
  258. '<p>foob̂<selection />ar</p>',
  259. '<p>foob<selection backward>̂</selection>ar</p>',
  260. { unit: 'codePoint', direction: 'backward' }
  261. );
  262. test(
  263. 'unicode support - combining mark multiple',
  264. '<p>fo<selection />o̻̐ͩbar</p>',
  265. '<p>fo<selection>o</selection>̻̐ͩbar</p>',
  266. { unit: 'codePoint' }
  267. );
  268. test(
  269. 'unicode support - surrogate pairs forward',
  270. '<p><selection />\uD83D\uDCA9</p>',
  271. '<p><selection>\uD83D\uDCA9</selection></p>',
  272. { unit: 'codePoint' }
  273. );
  274. test(
  275. 'unicode support surrogate pairs backward',
  276. '<p>\uD83D\uDCA9<selection /></p>',
  277. '<p><selection backward>\uD83D\uDCA9</selection></p>',
  278. { unit: 'codePoint', direction: 'backward' }
  279. );
  280. } );
  281. } );
  282. function test( title, input, output, options ) {
  283. it( title, () => {
  284. input = input.normalize();
  285. output = output.normalize();
  286. setData( document, input );
  287. // Creating new instance of selection instead of operation on engine.model.Document#selection.
  288. // Document's selection will throw errors in some test cases (which are correct cases, but only for
  289. // non-document selections).
  290. const testSelection = Selection.createFromSelection( document.selection );
  291. modifySelection( testSelection, options );
  292. expect( stringify( document.getRoot(), testSelection ) ).to.equal( output );
  293. } );
  294. }
  295. } );